常用排序算法总结

来源:互联网 发布:加工中心倒角编程实例 编辑:程序博客网 时间:2024/06/06 01:26

1.排序算法

1.1 插入排序

1.1.1 概念

1.1.2 代码

void insertionSort(int arr[], int len){    int insertPosi;    for (int i = 0; i < len; i++)    {        insertPosi = 0;                     //can't find the bigger position then keep the insertPosi 0        //find the insert position        for (int j = i - 1; j >= 0; j--)        {            if (arr[j] <= arr[i])            {                insertPosi = j + 1;         //insert at arr[j + 1], minimum of inserPosi is 1 int the loop;                break;            }        }        //same position as i, just keep it        if (insertPosi == i)            continue;        //copy from [j + 1, i - 1] -> [j + 2, i]        //namely copy from [insertPosi, i) -> [insertPosi + 1], i + 1)        int temp = arr[i];        for (int k = i - 1; k >= insertPosi; k--)        {            arr[k + 1] = arr[k];        }        arr[insertPosi] = temp;    }}

1.1.3 时间复杂度分析

查找插入位置的效率 在 O(1)~O(n)浮动
1)当输入序列已经有序时,该算法中的查找插入位置仅需O(1)时间,
即第一次判断 arr[j] <= arr[i],就成立了,就完成搜索了;
累计需要O(n)的时间,n*O(1)。
2)当输入序列完全逆序,则各次查找插入位置需要线性时间;
即每次都要判断线性长度次,比如将第i个元素插入到前面[0,i)的有序
序列中,需要搜索 i-1 次。
这样搜索完所有的长度,累计需要O(n2)时间,1+2+3+……+n的级别。

另外,向后复制的次数和搜索的向前遍历的次数是一样的,所以,总的时间是
2倍于搜索所需时间,常数2可以抹去。

总结
1)在等概率条件下,平均仍需要O(n2)时间。
2)输入敏感性很强,时间取决于逆序对的数目!!!

1.2 归并排序

1.2.1 概念


1.2.2 代码

/***************sort 2: mergeSort**************************************************************/void mergeSort(int arr[], int len){    mergeSortCore(arr, 0, len);}/*[lo, hi)*/void mergeSortCore(int arr[], int lo, int hi){    if (hi - lo < 2) return;    int mi = (lo + hi) / 2;    mergeSortCore(arr, lo, mi);    mergeSortCore(arr, mi, hi);    merge(arr, lo, mi, hi);}/*二路有序数组的归并,[lo, mi)[mi, hi)复杂度分析:每经过一次迭代,j和k至少有一个会+1,merge总体迭代不会超过O(n)*/void merge(int arr[], int lo, int mi, int hi){    int* A = arr + lo;    int lb = mi - lo;    int* B = new int[lb];    for (int i = 0; i < lb; B[i] = A[i++]);    int lc = hi - mi;    int* C = arr + mi;    //1.精简实现    //循环条件是复制出来的B序列还没全部插入排序    //如果B已经全部插入排序,C序列本身与A序列共享内存,不需要再做搬运复制工作    for (int i = 0, j = 0, k = 0; j < lb; )    {        //C序列还没全部插入,并且比B序列当前的小        if (k < lc && C[k] < B[j])            A[i++] = C[k++];        //C序列已经全部插入,或者 B序列当前的比C序列的小        if (lc <= k || B[j] <= C[k])            A[i++] = B[j++];    }    //2.复杂实现    // for (int i = 0, j = 0, k = 0; (j < lb) || (k < lc); )    // {    //  if ((j < lb) && (!(k < lc) || (B[j] <= C[k])))    //      A[i++] = B[j++];    //  if ((k < lc) && (!(j < lb) || (C[k] < B[j])))    //      A[i++] = C[k++];    // }    delete [] B;}

1.2.3 时间复杂度分析

归并排序

O(nlogn) 是根据递推式得出来的,根据分而治之策略,
T(n)=2T(n/2)+O(n)
=> T(n)=O(nlogn)

1.3 选择排序

selectMax()每次必须遍历整个无序前缀,耗时线性正比于前缀长度,累计需要O(n2)时间,1+2+3+……+n的级别。
输入不敏感,不论逆序对是多少,时间是固定的,即最好和最坏情况下的渐进效率相同,都为 θ(n2) 级别。
在列表形式实现中,与“冒泡排序”相比,比较次数是一样的,但是交换次数不一样。

void selectionSort(int arr[], int len){    int maxIdx = 0;    for (int i = len; i > 1; i--)    {        maxIdx = selectMax(arr, i);        swap(arr[i - 1], arr[maxIdx]);    }}int selectMax(int arr[], int len){    int maxIdx = 0;    for (int i = 1; i < len; i++)    {        if (arr[i] > arr[maxIdx])            maxIdx = i;    }    return maxIdx;}

1.4 桶排序

/**********sort 4 : bucketSort*******
O(n)
************************************/

void bucketSort(int arr[], int len){    int cntArr[GRADE_NUM];    memset(cntArr, 0, GRADE_NUM * sizeof(int));    for (int i = 0; i < len; i++)    {        ++cntArr[arr[i]];    }    //rebuild    int index = 0;    for (int i = 0; i < GRADE_NUM; i++)    {        for (int j = 0; j < cntArr[i]; j++)        {            arr[index++] = i;        }    }}

1.5 快速排序

1.5.1 概念

1)构造轴点,使得在轴点左边的小于轴点,在轴点右边的大于等于轴点
轴点位置就是排序后所在的位置
2)根据轴点分左右两段,两段独立,可以分别递归构造新轴点

title
title

1.5.2 代码

title
效率最高的构造轴点解法(partition):

title

void quickSort(int arr[], int len){    quickSortCore(arr, 0, len);}/*[lo, hi][lo]    (lo, mi]    (mi, k)     [k, hi]pivot     Less      Greater     Unsorted返回轴点位置*/int partition(int arr[], int lo, int hi){    //随机选择候选轴点,并将其交换到 arr[lo]位置    //轴点总是最大或者最小元素的时候,快速排序会退化为冒泡排序,    //为了减少这种退化的可能性,应该增加选取轴点的随机性    int randIdx = lo + rand() % (hi - lo + 1);    swap(arr[lo], arr[randIdx]);    //整体移位  => 用头尾交换来实现    int mi = lo;    //指向Less的最后一个元素    int pivot = arr[lo];    for (int k = lo + 1; k <= hi; k++)    {        if (pivot > arr[k])            swap(arr[++mi], arr[k]);    //Greater部分整体后移一个单位,然后在Less 和 Greater之间插入元素    }    swap(arr[mi], arr[lo]);             //Less部分整体前移一个单位,然后将pivot插入到真正的轴点位置    return mi;}//[lo, hi)void quickSortCore(int arr[], int lo, int hi){    if (hi - lo < 2) return;    int mi = partition(arr, lo, hi - 1);    //[lo, hi - 1]    quickSortCore(arr, lo, mi);             //[lo, mi)    quickSortCore(arr, mi + 1, hi);         //[m + 1, hi)}

1.5.3 时间复杂度分析

partition算法的时间复杂度为O(n)
根据partition算法估计总的快速排序的时间,通过递推式推算
下图O(n)指的就是partition算法的执行时间
title
title
最好O(nlogn),
最差O(n2),和冒泡排序相当
等概率下,平均还是O(nlogn),书上有详细推导。

无论是算法A还是算法C都不是稳定排序

1.6 希尔排序

2. 排序算法性能总结

排序算法 时间效率(最好情况) 时间效率(最坏情况) 时间效率(平均) 空间效率 适用范围 插入排序 O(n) O(n2) O(n2) O(1) 归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 选择排序 O(n2) O(n2) O(n2) O(1) 桶排序 O(n) O(n) O(n) O(L) L表示离散阶级个数 离散的阶级不多,然后会有很多相同大小数据的情况,如对年龄进行排序 快速排序 O(nlogn) O(n2) O(nlogn) O(1)
0 0
原创粉丝点击