复习数据结构:排序算法(五)——快速排序的各种版本

迷南。 2022-08-07 01:55 225阅读 0赞

之前已经比较熟悉快排的基本思想了,其实现的方式也有很多种。下面我们罗列一些常见的实现方式:

版本一:算法导论上的单向扫描,选取最后一个元素作为主元

  1. #include<iostream>
  2. using namespace std;
  3. int partition(int data[], int low, int high)
  4. {
  5. int pivot = data[high]; // let the last atom be the pivot
  6. int i = low - 1; // mark the pivot's true index: i+1
  7. for(int j = low; j < high; j++) // 数组是从low ~ high, 这里就要到high-1截止
  8. {
  9. if(data[j] <= pivot)
  10. {
  11. i++; // 说明pivot的位置要后移一位
  12. swap(data[i], data[j]); // 较小的数放前面,由于pivot是i+1, 所以把data[j]放在i的位置
  13. }
  14. }
  15. swap(data[i+1], data[j]); // 把主元放在合适的位置i+1,记住不是pivot,而是data[j]
  16. return i+1; // return the true index of pivot, that is, i+1
  17. }
  18. void quickSort(int data[], int low, int high)
  19. {
  20. if(low < high)
  21. {
  22. int k = partition(data, low, high);
  23. quickSort(data, low, k-1);
  24. quickSort(data, k+1, high);
  25. }
  26. }
  27. int main()
  28. {
  29. int data[] = {2, 6, 3, 8, 1, 4};
  30. quickSort(data, 0, 5);
  31. for(int i = 0; i < 6; i++)
  32. cout<<data[i]<<' ';
  33. cout<<endl;
  34. return 0;
  35. }

版本二:双向扫描,选取第一个元素作为主元

  1. #include<iostream>
  2. using namespace std;
  3. void print(int a[], int n){
  4. for(int j= 0; j<n; j++){
  5. cout<<a[j] <<" ";
  6. }
  7. cout<<endl;
  8. }
  9. void swap(int *a, int *b)
  10. {
  11. int tmp = *a;
  12. *a = *b;
  13. *b = tmp;
  14. }
  15. // 双向扫描法
  16. int partition(int a[], int low, int high)
  17. {
  18. int privotKey = a[low]; //基准元素
  19. while(low < high){ //从表的两端交替地向中间扫描
  20. while(low < high && a[high] >= privotKey) --high; //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端
  21. swap(&a[low], &a[high]);
  22. while(low < high && a[low] <= privotKey ) ++low;
  23. swap(&a[low], &a[high]);
  24. }
  25. print(a,10);
  26. return low;
  27. }
  28. void quickSort(int a[], int low, int high){
  29. if(low < high){
  30. int privotLoc = partition(a, low, high); //将表一分为二
  31. quickSort(a, low, privotLoc -1); //递归对低子表递归排序
  32. quickSort(a, privotLoc + 1, high); //递归对高子表递归排序
  33. }
  34. }
  35. int main(){
  36. int a[10] = {3,1,5,7,2,4,9,6,10,8};
  37. cout<<"初始值:";
  38. print(a,10);
  39. quickSort(a,0,9);
  40. cout<<"结果:";
  41. print(a,10);
  42. return 0;
  43. }

版本三:随机选取一个元素作为主元

  1. //随机选择主元的快排主要步骤是先随机选择主元的位置,然后把该主元与第一个元素调换,之后的步骤与拿第一个元素当做主元的算法一样
  2. #include<iostream>
  3. using namespace std;
  4. //交换两个元素值,咱们换一种方式,采取引用“&”
  5. void swap(int& a , int& b)
  6. {
  7. int temp = a;
  8. a = b;
  9. b = temp;
  10. }
  11. //返回属于[lo,hi)的随机整数
  12. int rand(int lo,int hi)
  13. {
  14. int size = hi-lo+1;
  15. return lo+ rand()%size;
  16. }
  17. //分割,换一种方式,采取指针a指向数组中第一个元素
  18. int RandPartition(int* data, int lo , int hi)
  19. {
  20. //普通的分割方法和随机化分割方法的区别就在于下面三行
  21. swap(data[rand(lo,hi)], data[lo]);
  22. int key = data[lo];
  23. int i = lo;
  24. for(int j=lo+1; j<=hi; j++)
  25. {
  26. if(data[j]<=key)
  27. {
  28. i = i+1;
  29. swap(data[i], data[j]);
  30. }
  31. }
  32. swap(data[i],data[lo]);
  33. return i;
  34. }
  35. //逐步分割排序
  36. void RandQuickSortMid(int* data, int lo, int hi)
  37. {
  38. if(lo<hi)
  39. {
  40. int k = RandPartition(data,lo,hi);
  41. RandQuickSortMid(data,lo,k-1);
  42. RandQuickSortMid(data,k+1,hi);
  43. }
  44. }
  45. int main()
  46. {
  47. const int N = 100; //此就是上文说所的“极限”测试。为了保证程序的准确无误,你也可以让N=10000。
  48. int *data = new int[N];
  49. for(int i =0; i<N; i++)
  50. data[i] = rand(); //同样,随机化的版本,采取随机输入。
  51. for(i=0; i<N; i++)
  52. cout<<data[i]<<" ";
  53. RandQuickSortMid(data,0,N-1);
  54. cout<<endl;
  55. for(i=0; i<N; i++)
  56. cout<<data[i]<<" ";
  57. cout<<endl;
  58. return 0;
  59. }

版本四:三数取中作为主元

上面的程序版本,其中算法导论上采取单向扫描中,是以最后一个元素为枢纽元素,即主元,而在Hoare版本及其几个变形中,都是以第一个元素、或中间元素为主元,最后,上述给的快速排序算法的随机化版本,则是以序列中任一一个元素作为主元。

那么,枢纽元素的选取,即主元元素的选取是否决定快速排序最终的效率列?

答案是肯定的,当我们采取data[lo],data[mid],data[hi]三者之中的那个第二大的元素为主元时,便能尽最大限度保证快速排序算法不会出现O(N^2)的最坏情况。这就是所谓的三数取中分割方法。当然,针对的还是那个Partition过程。

  1. //三数取中分割方法
  2. int RandPartition(int* a, int p , int q)
  3. {
  4. //三数取中方法的关键就在于下述六行,可以得到a[p], a[m], a[q]三个数中的中位数
  5. int m=(p+q)/2;
  6. if(a[p]<a[m])
  7. swap(a[p],a[m]);
  8. if(a[q]<a[m])
  9. swap(a[q],a[m]);
  10. if(a[q]<a[p])
  11. swap(a[q],a[p]);
  12. int key = a[p];
  13. int i = p;
  14. for(int j = p+1; j <= q; j++)
  15. {
  16. if(a[j] <= key)
  17. {
  18. i = i+1;
  19. if(i != j)
  20. swap(a[i], a[j]);
  21. }
  22. }
  23. swap(a[i],a[p]);
  24. return i;
  25. }
  26. void QuickSort(int data[], int lo, int hi)
  27. {
  28. if (lo<hi)
  29. {
  30. int k = RandPartition(data, lo, hi);
  31. QuickSort(data, lo, k-1);
  32. QuickSort(data, k+1, hi);
  33. }
  34. }

版本五:非递归版本

  1. //递归的本质是什么?对了,就是借助栈,进栈,出栈来实现的。
  2. if( (key-lo)<(key-hi) )
  3. {
  4. st.push(key+1);
  5. st.push(hi);
  6. hi=key-1;
  7. }
  8. else
  9. {
  10. st.push(lo);
  11. st.push(key-1);
  12. lo=key+1;
  13. }
  14. }
  15. if(st.empty())
  16. return;
  17. hi=st.top();
  18. st.pop();
  19. lo=st.top();
  20. st.pop();
  21. }while(1);
  22. }

参考:

http://blog.csdn.net/hguisu/article/details/7776068

http://blog.csdn.net/xiazdong/article/details/8462393

http://blog.csdn.net/v\_JULY\_v/article/details/6262915

发表评论

表情:
评论列表 (有 0 条评论,225人围观)

还没有评论,来说两句吧...

相关阅读