排序--面经

视频学习网址:点击打开链接

参考书籍《算法导论》

Pre比较:

理论时间:

                          堆排序      归并排序        快速排序
最坏时间        O(nlogn)     O(nlogn)        O(n^2)
最好时间        O(nlogn)     O(nlogn)        O(nlogn)
平均时间        O(nlogn)     O(nlogn)        O(nlogn)

空间复杂度     O(1)             O(n)              ???这里不对啊,我觉得是O(1)啊(ps:这里的空间复杂度还包括了递归的过程,由于快排是递归实现的,所以有log(n)到n的空间复杂度,堆排序如果用递归实现也是logn的空间复杂度,不过堆排序可以改成非递归实现)


实际测量:

4种非平方级的排序:
希尔排序,堆排序,归并排序,快速排序

我测试的平均排序时间:数据是随机整数,时间单位是秒
数据规模    快速排序    归并排序    希尔排序    堆排序
1000万       0.75           1.22          1.77          3.57
5000万       3.78           6.29          9.48         26.54  
1亿             7.65          13.06        18.79        61.31

堆排序是最差的。
这是算法硬伤,没办法的。因为每次取一个最大值和堆底部的数据(记为X)交换,重新筛选堆,把堆顶的X调整到位,有很大可能是依旧调整到堆的底部(堆的底部X显然是比较小的数,才会在底部),然后再次和堆顶最大值交换,再调整下来。
从上面看出,堆排序做了许多无用功。


排序算法稳定性定义:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。

堆排序、快速排序、希尔排序、直接选择排序   不是稳定的排序算法,(快排和直接选择排序都是在发生交换的时候破坏的,希尔排序由于步长大于1时开头就会跳过一些元素,堆排序在维护堆的时候会破坏稳定性)

而基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序、计数排序   是稳定的排序算法。

参考:百度百科

参考视频:点击打开链接

1 冒泡排序:两两比较,大的放后;关注for循环中的两个变量的变化范围;时间复杂度O(n^2)

[cpp]  view plain  copy
  1. #include <iostream>  
  2.   
  3. class BubbleSort  
  4. {  
  5. public:  
  6.     BubbleSort(){};//默认构造函数  
  7.     BubbleSort(BubbleSort &other){};//拷贝构造函数  
  8.   
  9.     //冒泡排序函数  
  10.     int *bubbleSort(int *pa, int n);  
  11. private:  
  12.   
  13. };  
  14.   
  15.   
  16. //冒泡排序函数  
  17. int *BubbleSort::bubbleSort(int *pa, int n)  
  18. {  
  19.     int temp = 0;  
  20.     //for循环,两两比较排序  
  21.     for (int j = 0; j < n - 1; j++)  
  22.     {  
  23.         for (int k = 0; k < n - 1 - j; k++)  
  24.         {  
  25.             if (pa[k]>pa[k + 1])  
  26.             {  
  27.                 temp = pa[k];  
  28.                 pa[k] = pa[k + 1];  
  29.                 pa[k + 1] = temp;  
  30.             }  
  31.         }  
  32.     }  
  33.   
  34.     //返回  
  35.     return pa;  
  36. }  

2 插入排序:逐个选取数组中元素,与前面元素比较插入;时间复杂度O(n^2)

[cpp]  view plain  copy
  1. class InsertionSort {  
  2. public:  
  3.     int* insertionSort(int* A, int n)   
  4.     {  
  5.         // write code here  
  6.         int key = 0;  
  7.         //遍历数组中每一个元素  
  8.         for (int i = 1; i < n; i++)  
  9.         {  
  10.             key = A[i];  
  11.             int j = i - 1;  
  12.             //与前面的元素逐个比较,大于则插入  
  13.             while (j>=0 && A[j]>key)  
  14.             {  
  15.                 A[j + 1] = A[j];  
  16.                 j--;  
  17.             }  
  18.             A[j + 1] = key;  
  19.         }  
  20.   
  21.         return A;  
  22.     };  
  23.   
  24. private:  
  25.   
  26. };  

3 选择排序:选择最大值,放到数组的后面;时间复杂度O(n^2)

[cpp]  view plain  copy
  1. class SelectionSort {  
  2. public:  
  3.     int* selectionSort(int* A, int n) {  
  4.         // write code here  
  5.         int maxKey = 0;  
  6.         int maxInd = 0;  
  7.         int temp = 0;  
  8.         //每次选择最大的,放在数组的后面  
  9.         for (int i = n - 1; i >= 0; i--)  
  10.         {     
  11.             maxKey = A[0];  
  12.             maxInd = 0;  
  13.   
  14.             //找出最大的  
  15.             for (int j = 0; j <= i; j++)  
  16.             {  
  17.                 if (A[j] > maxKey)  
  18.                 {  
  19.                     maxKey = A[j];  
  20.                     maxInd = j;  
  21.                 }  
  22.             }  
  23.   
  24.             //交换 最大值 和 查找范围末尾位置的值  
  25.             temp = A[i];  
  26.             A[i] = A[maxInd];  
  27.             A[maxInd] = temp;  
  28.         }  
  29.         return A;  
  30.     }  
  31.       
  32. };  

4 归并排序:递归分解为子问题,将子问题合并。时间复杂度O(nlogn),空间上需要动态分配内存辅助,额外的空间需求

[cpp]  view plain  copy
  1. class MergeSort  
  2. {  
  3. public:  
  4.     int *mergeSort(int *A,int n)  
  5.     {  
  6.         mergeSortAC(A, 0, n - 1);  
  7.         return A;  
  8.     }  
  9.       
  10. private:  
  11.     //将pa指向的数组,划分成两个小数组,排序后再合并  
  12.     int mergeSortAC(int *pa, int a, int c);  
  13.   
  14.     //合并函数  
  15.     int merge(int *pa, int a, int b, int c);  
  16. };  
  17.   
  18.   
  19. //将pa指向的数组,划分成两个小数组,排序后再合并  
  20. int MergeSort::mergeSortAC(int *pa, int a, int c)  
  21. {  
  22.     //边界条件:只有一个元素,返回  
  23.     if (a >= c) return 0;  
  24.   
  25.     int b = (a + c) >> 1;  
  26.   
  27.     //对[a,b],[b+1,c]的两个数组分别排序,递归调用  
  28.     mergeSortAC(pa, a, b);  
  29.     mergeSortAC(pa, b + 1, c);  
  30.   
  31.     //合并两个已经排好序的数组  
  32.     merge(pa, a, b, c);  
  33.   
  34.     return 0;  
  35. }  
  36. //合并函数  
  37. int MergeSort::merge(int *pa, int a, int b, int c)  
  38. {  
  39.     //分配两个动态数组  
  40.     int *pb = new int[b - a + 2];  
  41.     assert(pb != NULL);  
  42.     int *pc = new int[c - b + 1];  
  43.     assert(pc != NULL);  
  44.   
  45.     //将要排序的部分复制到动态数组中  
  46.     int i = a;  
  47.     int j = b + 1;  
  48.   
  49.     while (i <= b)  
  50.     {  
  51.         pb[i - a] = pa[i];  
  52.         i++;  
  53.     }  
  54.     while (j <= c)  
  55.     {  
  56.         pc[j - b - 1] = pa[j];  
  57.         j++;  
  58.     }  
  59.   
  60.     //为动态数组添加末尾无穷大值  
  61.     pb[b - a + 1] = INT_MAX;  
  62.     pc[c - b] = INT_MAX;  
  63.   
  64.     //逐个比较复制到pa中  
  65.     i = j = 0;  
  66.     for (int k = a; k <= c; k++)  
  67.     {  
  68.         if (pb[i] < pc[j])  
  69.         {  
  70.             pa[k] = pb[i];  
  71.             i++;  
  72.         }  
  73.         else  
  74.         {  
  75.             pa[k] = pc[j];  
  76.             j++;  
  77.         }  
  78.     }  
  79.   
  80.     //释放动态内存  
  81.     delete[] pb;  
  82.     delete[] pc;  
  83.   
  84.     //返回  
  85.     return 0;  
  86.   
  87. }  

5 快速排序:随机抽取一个数,小的放左边,大的放右边。利用小于等于区间,可以原址排序。使用随机函数,引入随机选择。理论最差情况,时间复杂度O(n^2),期望复杂度是O(nlogn),实际使用中,快排的速度是最快的,而且由于是原址排序,所以空间需求少,不像归并排序需要O(n)的辅助空间。

[cpp]  view plain  copy
  1. #include <stdlib.h>  
  2. #include <time.h>  
  3.   
  4. class QuickSort  
  5. {  
  6. public:  
  7.     int *quickSort(int *A, int n)  
  8.     {  
  9.         quickSortBC(A,0,n-1);  
  10.         return A;  
  11.     }  
  12.     int *quickSortBC(int *A, int b, int c)  
  13.     {  
  14.         //边界条件  
  15.         if (b >= c) return 0;  
  16.   
  17.         int i = 0;  
  18.         i = partition(A, b, c);//划分  
  19.   
  20.         quickSortBC(A, b, i-1);//对划分的子集,快速排序  
  21.         quickSortBC(A, i+1, c);  
  22.   
  23.         return 0;  
  24.     }  
  25.     //划分函数  
  26.     int partition(int *A, int b, int c)  
  27.     {  
  28.         //边界条件  
  29.         if (b >= c) return 0;  
  30.   
  31.         //为快排加入随机选择  
  32.         srand((unsigned)time(0));  
  33.         int ind = b + rand() % (c - b + 1);//ind取值范围[b,c]  
  34.         int key = A[ind];  
  35.         //将ind对应的元素,换到数组末尾  
  36.         A[ind] = A[c];  
  37.         A[c] = key;  
  38.   
  39.         int temp = key;  
  40.         int i = b - 1;  
  41.         int j = b - 1;  
  42.         while (j < c)  
  43.         {  
  44.             j++;  
  45.             if (A[j] <= key)  
  46.             {  
  47.                 i++;  
  48.                 temp = A[i];  
  49.                 A[i] = A[j];  
  50.                 A[j] = temp;  
  51.             }  
  52.         }  
  53.   
  54.         //返回key值的索引  
  55.         return i;  
  56.     }  
  57.   
  58. private:  
  59.   
  60. };  


6 堆排序:利用最大堆,每次取堆根,然后维护堆;时间复杂度上界是O(nlogn),堆排在实际中并不快,做了很多无用的比较;

[cpp]  view plain  copy
  1. class HeapSort  
  2. {  
  3. public:  
  4.     int *heapSort(int *A, int n);  
  5. private:  
  6.     int buildHeap(int *A, int size);//建堆  
  7.     int maxHeap(int *A, int ind, int size);//维护最大堆  
  8.     inline int exchange(int *A, int a, int b);//交换两个元素  
  9. };  
  10.   
  11.   
  12. int *HeapSort::heapSort(int *A, int n)  
  13. {  
  14.     //将最小元素换到A[0],因为A[0]是不参与建堆的  
  15.     int minEle = A[0];  
  16.     int ind = 0;  
  17.     for (int i = 0; i < n; i++)  
  18.     {  
  19.         if (minEle>A[i])  
  20.         {  
  21.             minEle = A[i];  
  22.             ind = i;  
  23.         }  
  24.     }  
  25.     A[ind] = A[0];  
  26.     A[0] = minEle;  
  27.   
  28.     //建堆  
  29.     buildHeap(A, n - 1);  
  30.   
  31.     //循环取堆根放到数组A末尾,维护堆  
  32.     for (int size = n - 1; size > 1;)  
  33.     {  
  34.         exchange(A, 1, size);  
  35.         size--;  
  36.         maxHeap(A, 1, size);  
  37.     }  
  38.   
  39.     return A;  
  40. }  
  41. int HeapSort::buildHeap(int *A, int size)//建堆  
  42. {  
  43.     //from size/2 to 1  
  44.     for (int i = size >> 1; i > 0; i--)  
  45.     {  
  46.         maxHeap(A, i, size);  
  47.     }  
  48.   
  49.     return 0;  
  50. }  
  51. int HeapSort::maxHeap(int *A, int ind, int size)//维护最大堆  
  52. {  
  53.     int largest = ind;  
  54.     int left = (ind << 1);  
  55.     int right = (ind << 1) + 1;  
  56.   
  57.     if (left <= size && A[left] > A[ind])//与左孩子比较  
  58.     {  
  59.         largest = left;  
  60.     }  
  61.     if (right <= size && A[right] > A[largest])//与右孩子比较  
  62.     {  
  63.         largest = right;  
  64.     }  
  65.     if (largest != ind)  
  66.     {  
  67.         exchange(A, largest, ind);  
  68.         maxHeap(A, largest, size);  
  69.     }  
  70.     return 0;  
  71. }  
  72. inline int HeapSort::exchange(int *A, int a, int b)  
  73. {  
  74.     int temp = A[a];  
  75.     A[a] = A[b];  
  76.     A[b] = temp;  
  77.     return 0;  
  78. }  


7 希尔排序


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值