内部排序法总结

简介:

1.冒泡排序(Bubble Sort)

冒泡排序方法是最简单的排序方法。这种方法的基本思想是,将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮。在冒泡排序算法中我们要对这个“气泡”序列处理若干遍。所谓一遍处理,就是自底向上检查一遍这个序列,并时刻注意两个相邻的元素的顺序是否正确。如果发现两个相邻元素的顺序不对,即“轻”的元素在下面,就交换它们的位置。显然,处理一遍之后,“最轻”的元素就浮到了最高位置;处理二遍之后,“次轻”的元素就浮到了次高位置。在作第二遍处理时,由于最高位置上的元素已是“最轻”元素,所以不必检查。一般地,第i遍处理时,不必检查第i高位置以上的元素,因为经过前面i-1遍的处理,它们已正确地排好序。
冒泡排序是稳定的。算法时间复杂度是O(n^2)。

复制代码
//冒泡排序(相邻比较法)
void BubbleSort(int a[],int n)
{
    int i,j;
    int temp;
    for(i = 0; i < n-1; i++)
        for(j = 0; j < n-i-1; j++)
            if(a[j] > a[j+1])
            {
                temp = a[j];
                a[j] = a[j+1];
                a[j+1] = temp;
            }
}
//改进的冒泡排序
void ImprovedBubbleSort(int a[], int n)
{
    int i,j;
    int temp;
    bool change = false;
    for(i = 0; i < n-1; i++)
    {
        change = false;
        for(int j = 0; j < n-i-1; j++)
        if(a[j] > a[j+1])
        {
            temp = a[j];
            a[j] = a[j+1];
            a[j+1] = temp;
            change = true;
        }
        if ( !change ) break;
    }
}
//双向冒泡排序(鸡尾酒排序)
void CocktailSort(int a[], int n)
{
    int top = n - 1;
    int bottom = 0;
    bool flag = true;
    int i, j;
    while(flag)
    {
        flag = false;
        //从小到大,升序
         for(i = bottom; i < top; i++)
        {
            if(a[i] > a[i+1])
            {
                swap(a[i], a[i+1]);
                flag = true;
            }
        }
        top--;

        //从大到小,降序
        for(j = top; j > bottom; j--)
        {
            if(a[j] < a[j-1])
            {
                swap(a[j], a[j-1]);
                flag = true;
            }
        }
        bottom++;
    }
}
复制代码

An example of bubble sort. Starting from the beginning of the list, compare every adjacent pair, swap their position if they are not in the right order (the latter one is smaller than the former one). After each iteration, one less element (the last one) is needed to be compared until there are no more elements left to be compared.

2.选择排序(Selection Sort)

选择排序的基本思想是对待排序的记录序列进行n-1遍的处理,第i遍处理是将L[i..n]中最小者与L[i]交换位置。这样,经过i遍处理之后,前i个记录的位置已经是正确的了。
选择排序是不稳定的。算法复杂度是O(n^2 )。

复制代码
//选择排序
void SelectSort(int a[],int n)
{
    int temp;
    int i,j;
    for(i = 0; i < n; i++)
    {
        int k = i;
        for(j = i+1; j < n; j++)
            if(a[j] < a[k]) k = j;
        if(k != i)
        {
           temp = a[i];
           a[i] = a[k];
           a[k] = temp;
        }
    }
}
复制代码

Selection sort animation. Red is current min. Yellow is sorted list. Blue is current item.

3.插入排序(Insertion Sort)

插入排序的基本思想是,经过i-1遍处理后,L[1..i-1]己排好序。第i遍处理仅将L[i]插入L[1..i-1]的适当位置,使得L[1..i]又是排好序的序列。要达到这个目的,我们可以用顺序比较的方法。首先比较L[i]和L[i-1],如果L[i-1]<=L[i],则L[1..i]已排好序,第i遍处理就结束了;否则交换L[i]与L[i-1]的位置,继续比较L[i-1]和L[i-2],直到找到某一个位置j(1<=j<=i-1),使得L[j] <=L[j+1]时为止。
直接插入排序是稳定的。算法时间复杂度是O(n^2)。

复制代码
//插入排序
void InsertSort(int a[], int n)
{
    int i, j;
    int temp;
    for(i = 1; i < n; i++)
    {
        temp = a[i];
        j = i-1;
        while(j >= 0 && a[j] > temp;)
        {
            a[j+1] = a[j];
            --j;
        }
        a[j+1] = temp;
    }
}

//递归的插入排序
void RecursiveInsertSort(int a[], int n)
{
    int i, j, key;
    if(n > 1)
    {
        RecursiveInsertSort(a,n-1);
    }
    key = a[n-1];
    i = n-2;
    while(i >= 0 && a[i] > key)
    {
        a[i+1] = a[i];
        i--;
    }
    a[i+1] = key;
}

//折半插入排序
void BinInsertSort(int a[], int n)
{
    int i,j;
    for(i = 1; i < n; i++)
    {
        // 在a[0..i-1]中折半查找插入位置使a[high]<=a[i]<a[high+1..i-1]
        int low = 0, high = i-1, m = 0;
        while(low <= high)
        {
            m = m + (high-low)/2;
            if (a[i] < a[m]) high = m-1;
            else low = m+1;
        }
        // 向后移动元素a[high+1..i-1],在a[high+1]处插入a[i]
        int x = a[i];
        for (j = i-1; j > high; j--)
            a[j+1] = a[j];
        a[high+1] = x;     // 完成插入
    }
}
复制代码

A graphical example of insertion sort.

4.希尔排序(Shell Sort)

先将待排序列分割成若干个子序列,分别进行直接插入排序,基本有序后再对整个序列进行直接插入排序。
希尔排序是不稳定的。时间复杂度大约为O(n^3/2)。

复制代码
//希尔排序:shell排序的核心仍然使用插入排序
void ShellSort(int a[], int n)
{
    int dk = n/2;
    int i,j;
    while(dk >= 1)
    {
        // 一趟希尔排序,对dk个序列分别进行插入排序
        for(i = dk; i < n; ++i)
        {
            int x = a[i];
            for (j = i-dk; j >= 0 && a[j] > x; j -= dk )
                a[j+dk] = a[j];
            a[j+dk] = x;
        }
        dk = dk/2;  // 缩小增量
    }
}
复制代码

5.堆排序(Heap Sort)

堆排序是一种树形选择排序,在排序过程中,将A[n]看成是完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。对N个元素从小到大排序建立大根堆,然后交换堆顶与最后一个元素,将剩下的N-1个元素调整为大根堆,执行N-1此这样的操作。
堆排序是不稳定的。算法时间复杂度O(nlogn)。

复制代码
//调整大根堆
void HeapAdjust(int data[],int nStart, int nLen)
{
    int nMaxChild = 0;
    int Temp;

    while((2*nStart+1) < nLen)
    {
        nMaxChild = 2*nStart+1;
        if((2*nStart+2) < nLen)
        {
            //比较左子树和右子树,记录最大值的Index
            if (data[2*nStart+1] < data[2*nStart+2])
            {
                nMaxChild = 2*nStart+2;
            }
        }
        //change data
        if(data[nStart] < data[nMaxChild])
        {
            //交换nStart与nMaxChild的数据
              Temp = data[nStart];
            data[nStart] = data[nMaxChild];
            data[nMaxChild] = Temp;

            //堆被破坏,需要重新调整
              nStart = nMaxChild;
        }
        else
        {
            //比较左右孩子均小则堆未破坏,不再需要调整
              break;
        }
    }
}

//堆排序 从小到大排序建立大顶堆
void HeapSort(int data[],int nLen)
{
    int i;
    int nTemp;
    //建立堆
    for(i = nLen/2-1; i >= 0; i--)
    {
        HeapAdjust(data, i, nLen);
    }
    for(i = nLen-1; i > 0; i--)
    {
        //交换堆顶元素和最后一个元素
         nTemp = data[0];
        data[0] = data[i];
        data[i] = nTemp;

        //将data[0...i]重写建成大根堆
         HeapAdjust(data, 0, i);
    }
}
复制代码

6.快速排序(Quick Sort)

快速排序是对冒泡排序的一种本质改进。它的基本思想是通过一趟扫描后,使得排序序列的长度能大幅度地减少。在冒泡排序中,一次扫描只能确保最大数值的数移到正确位置,而待排序序列的长度可能只减少1。快速排序通过一趟扫描,就能确保某个数(以它为基准点吧)的左边各数都比它小,右边各数都比它大。然后又用同样的方法处理它左右两边的数,直到基准点的左右只有一个元素为止。

快速排序是不稳定的。最理想情况算法时间复杂度O(nlog2n),最坏O(n^2)。

复制代码
//快速排序
void QuickSort(int a[], int low, int high)
{
    if(low < high)
    {
        // 划分
        int pivot = a[low];
        int i = low; int j = high;
        while(i < j)
        {
            while(i<j && a[j] >= pivot)  j--;
            a[i] = a[j];
            while(i<j && a[i] <= pivot)  i++;
            a[j] = a[i];
        }
        a[i] = pivot;
        // 对子序列快排
        QuickSort(a, low, i-1);
        QuickSort(a, i+1, high);
    }
}

//快速排序法的划分操作
int Partition(int vec[],int low,int high)
{
     //任选元素作为轴,这里选首元素
     int pivot = vec[low];
     while(low < high)
     {
         while(low < high && vec[high] >= pivot)
             high--;
         vec[low] = vec[high];
         while(low < high && vec[low] <= pivot)
             low++;
         vec[high] = vec[low];
     }
     //此时low==high
     vec[low] = pivot;
     return low;
}

//in-place partition algorithm
//http://en.wikipedia.org/wiki/Quicksort
int in_place_partition(int* array, int left, int right)
{
    int pivot_index = (right-left)/2;
    int pivot = array[pivot_index];
    swap(array[pivot_index], array[right]);
    int index = left;
    for(int i = left; i < right; i++)
    {
        if (array[i] < pivot)    //升序
         {
            swap(array[index], array[i]);
            ++index;
        }
    }
    swap(array[right], array[index]);
    return index;
}

void Qsort(int* array, int left, int right)
{
    if (left >= right)
        return;
    int index = Partition(array, left, right);
    Qsort(array, left, index - 1);
    Qsort(array, index + 1, right);
}
复制代码

非递归的快速排序:用栈保存每个待排序子串的首尾元素下标,下一次循环时取出这个范围,对这段子序列进行partition操作,直到所有的子串都排好序,栈也为空了。

复制代码
//非递归快速排序
void QuickSort2(int vec[],int low,int high)
{
    stack<int> st;
    if(low >= high) return;

    int mid = Partition(vec,low,high);
    if(low < mid-1)
    {
        st.push(low);
        st.push(mid-1);
    }
    if(mid+1 < high)
    {
        st.push(mid+1);
        st.push(high);
    }
    //用栈保存每个待排序子串的首尾元素下标,下一次循环时取出这个范围,对这段子序列进行partition操作
    while(!st.empty())
    {
        int q = st.top();
        st.pop();
        int p=st.top();
        st.pop();
        mid = Partition(vec,p,q);
        if(p < mid-1)
        {
            st.push(p);
            st.push(mid-1);
        }
        if(mid+1 < q)
        {
            st.push(mid+1);
            st.push(q);
        }
    }
}
复制代码

7.归并排序(Merge Sort)

基本思想是合并两个有序表,设有两个有序(升序)序列存储在同一数组中相邻的位置上,不妨设为A[l..m],A[m+1..h],将它们归并为一个有序数列,并存储在A[l..h]。
归并排序的时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlog2n)。

归并排序是稳定的排序算法。

复制代码
//将有序序列a[low..mid]和a[mid+1..high]归并到a[low..high]。
void Merge(int a[], int low, int mid, int high)
{
    // 归并到b[]
    int i = low;
    int j = mid+1;
    int k = 0;
    int *b = new int[high-low+1];
    while(i <= mid && j <= high)
    {
        if (a[i] <= a[j]) { b[k++] = a[i++]; }
        else  { b[k++] = a[j++]; }
    }
    // 归并剩余元素
    while(i <= mid)  b[k++] = a[i++];
    while(j <= high)  b[k++] = a[j++];
    // 从b[]复制回a[]
    for(i = 0; i <= high-low; ++i)
        a[low+i] = b[i];
    delete []b;
}

//归并排序
//http://en.wikipedia.org/wiki/Mergesort
void MergeSort(int a[], int low, int high)
{
    if(low >= high)  return;
    else
    {
        int mid = (low+high)/2;
        MergeSort(a,low,mid);
        MergeSort(a,mid+1,high);
        Merge(a,low,mid,high);
    }
}

//自底向上的归并排序
void MergeSort2(int a[], int n)
{
    int s,i,t = 1;
    while(t < n)
    {
        s = t;  t = s*2;
        for(i=0; i+t<=n; i+=t)
            Merge(a,i,i+s-1,i+t-1);
        if(i+s < n)
            Merge(a,i,i+s-1,n-1);
    }
}
复制代码

归并排序在最坏的情况下都是O(NlogN)的时间复杂度,缺点是Merge的时候要有O(N)的额外的空间,如何改进?

使用原地归并排序 In-place Merge Sort: http://www.ahathinking.com/archives/103.html

复制代码
//reverse array
void reverse(int arr[], int size)
{
    int left = 0;
    int right = size -1;
    while(left < right)
    {
        int temp = arr[left];
        arr[left++] = arr[right];
        arr[right--] = temp;
    }
}

// swap [arr,arr+headSize) and [arr+headSize,arr+headSize+endSize)
void SwapMemory(int arr[], int headSize, int endSize)
{
    reverse(arr, headSize);
    reverse(arr + headSize, endSize);
    reverse(arr, headSize + endSize);
}

//原地归并
void Inplace_Merge(int arr[], int beg, int mid, int end)
{
    int i = beg;     // 指示有序串1
    int j = mid + 1; // 指示有序串2
    while(i < j && j <= end)
    {
        while(i < j && arr[i] <= arr[j])
        {
            ++i;
        }
        int index = j;
        while(j <= end && arr[j] <= arr[i])
        {
            ++j;
        }
        SwapMemory(&arr[i], index-i, j-index);//swap [i,index) and [index,j)
        i += (j-index);
    }
}

//原地归并排序
void Inplace_MergeSort(int arr[], int beg, int end)
{
    if(beg < end)
    {
        int mid = (beg + end) / 2;
        Inplace_MergeSort(arr, beg, mid);
        Inplace_MergeSort(arr, mid+1, end);
        Inplace_Merge(arr, beg, mid, end);
    }
}
复制代码

 

An example of merge sort. First divide the list into the smallest unit (1 element), then compare each element with the adjacent list to sort and merge the two adjacent lists. Finally all the elements are sorted and merged.

 

参考:

http://en.wikipedia.org/wiki/Sorting_algorith


    本文转自阿凡卢博客园博客,原文链接:http://www.cnblogs.com/luxiaoxun/archive/2012/09/01/2666677.html,如需转载请自行联系原作者


相关文章
|
6月前
|
C++
成员初始化表的执行顺序与顺写顺序无关
成员初始化表的执行顺序与顺写顺序无关
53 0
|
6月前
|
搜索推荐 算法 Python
如何实现归并排序算法? 要求:编写一个Python函数,输入一个无序列表,返回排序后的列表。
如何实现归并排序算法? 要求:编写一个Python函数,输入一个无序列表,返回排序后的列表。
52 2
|
6月前
|
数据处理
利用Stream流将取到的对象List<对象>形式数据进行分组统计转变成Map<分组条件,数量统计>形式
利用Stream流将取到的对象List<对象>形式数据进行分组统计转变成Map<分组条件,数量统计>形式
60 0
|
2月前
|
索引 Python
解密可迭代对象的排序问题
解密可迭代对象的排序问题
26 0
各种基础排序的超详细解析及比较
各种基础排序的超详细解析及比较
39 0
|
6月前
|
存储 算法
轮转数组问题:如何实现数组“整体逆序,内部有序”?“三步转换法”妙转数组(一)
这篇内容讲述了数组向右轮转k个位置的问题,主要分为两种解决思路。第一种是“轮转k次法”,直接在原数组上操作,每次循环k次,将数组末尾元素移到开头,时间复杂度为O(N*K),效率较低。第二种是“额外数组法”,使用额外数组存储部分元素,然后移动原数组元素,最后归还额外数组元素,时间复杂度和空间复杂度均为O(N)。文章还介绍了更高效的“三步转换法”,通过三次逆序操作实现数组轮转,分别是逆置后k个元素、逆置前n-k个元素以及整体逆置,这种方法时间复杂度为O(N)且空间复杂度为O(1)。
73 1
|
6月前
|
C语言
轮转数组问题:如何实现数组“整体逆序,内部有序”?“三步转换法”妙转数组 (二)
这是一个关于字符串处理的问题,要求将一句话中的单词顺序倒置,但保持单词内部的字符顺序不变。例如,"I like beijing." 变为 "beijing. like I"。解决方法可以分为三个步骤:分块、内部逆序和整体逆序。代码示例使用C语言实现,通过指针和while循环操作字符串数组。最终总结提到,无论先逆置哪个部分,只要确保所有部分都逆置过,结果都是相同的。
52 0
|
6月前
13.从M个球中取出N个球的所有组合情况
13.从M个球中取出N个球的所有组合情况
38 0
|
6月前
|
搜索推荐
排序的概念及其运用
排序的概念及其运用
排序的概念及其运用
|
JavaScript
数组双重去重的方式四先排序在对比
数组双重去重的方式四先排序在对比
46 0