- 快速排序,先理解其思想,见维基百科--快速排序。找几个例子试了一下。基本是:给定一个数组,编号从0到n,找其中一个数字作为枢轴,比该枢轴小的数都放在左边,比该枢轴大的都放在右边。之后再分而治之,分别对左边和右边的数组进行排序。
- 转化为代码。
#include<stdio.h> #include<stdlib.h> void quicksort(int*,int,int); main() { int* a = NULL; int size; int i=0; int left=0; int right; a = (int *)malloc(sizeof(int)*100); printf("PLease input the array:\n"); while(1) //读入数组 { scanf("%d",&a[i++]); if(getchar() == '\n') { break; } } right = i-1; //右标 size = i; //数组大小 quicksort(a,left,right); //对区间[left,right]升序排序 for(i=0;i < size;i++) //打印 printf("%d ",a[i]); free(a); return 0; } void quicksort(int a[],int left,int right) //原地排序 从left到right { int lastleft = left; int lastright = right; int pivot= a[left]; while(left< right) //考虑包不包含边界=的条件 { if(a[left] < pivot) //这里包含等吗? { left++; } //将等于的情况都放到右边 else { if(a[right] > pivot) { right--; } else { if(a[right]<pivot) { a[right] = a[right]^a[left]; a[left] = a[right]^a[left]; a[right] = a[right]^a[left]; } // else // { // right--; //解决重复的问题。 在接下来的排序中,右边都是比上一轮排序的枢轴还大的数 // } } } } //循环跳出的条件是left = right,且该位置的数已经排好 if(lastleft < left-1) { quicksort(a,lastleft,left-1); } if(right+1 < lastright) { quicksort(a,right+1,lastright); } //return; } //1 2 0 0 0 -3 -3 3 -3 3 9 -3 3 0 0 1 3 <span style="font-family: Arial, Helvetica, sans-serif;">//0 -3 -3 3 -3 3 // 5 6 7 1 2 不成功的测试用例 </span>
- 以上是第一次编写出来的程序,不能确保每次排序之后,有一个元素被放到正确的位置上,如 5 6 7 1 2中排序之后是 1 2 7 5 6.
- 难点(个人感觉):单趟排序中,对于数组中出现相同元素的处理,如5 5 3 2 6.尤其是待比较的元素与枢轴大小相同时;边界问题,<= 还是< ;循环什么时候结束。结束的边界条件;整个大的排序何时结束,结束条件。
- 在这次编程中,如何验证程序的正确性的思考。大量的测试用例。形式化的证明。有对此有研究的朋友欢迎赐教
-
经典正确的程序:参考了知乎的回答,https://www.zhihu.com/question/23171968:代码如下
void quicksort(int l, int u){ int i, m; if(l >= u) return; m = l; for(i = l+1; i<= u; i++) if(x[i] < x[l]) // buggy! swap(++m, i); swap(l, m); quicksort(l, m-1); quicksort(m+1, u); } <pre name="code" class="cpp" style="color: rgb(34, 34, 34); font-size: 13px; line-height: 17.68px;">/*作者:dontbeatmycat 链接:https://www.zhihu.com/question/23171968/answer/23804982 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。*/
-
Jon Bently在Beautiful Code上的代码。l是low,最左的序号,u是upper,最右的序号。核心:循环不变式,数学归纳的思想:确保[l+1,m]都是比枢轴小的数字。在之后发现的比枢轴小的数都交换到m+1的位置。始终确保[l+1,m]是比枢轴小的数字,之后交换枢轴和m位置上的数,确保了每次排序都将一个数字放在了正确的位置上。
-
这个partition在大量duplicate key的情况下,会把这些key全部放到左边或者右边。导致不公平的切分。越不公平的切分,越会下降这个算法的性能。知乎作者提供了解决这个问题的办法,将数组分成3部分,比pivot小的,比pivot大的,和pivot相等的。相当于一次排序之后,将和枢轴相等的数全部放在了中间正确的位置上。
<pre name="code" class="java">public static void sort(Comparable[] a) { StdRandom.shuffle(a);//随机填充数组 sort(a, 0, a.length - 1); assert isSorted(a); //断言 } // quicksort the subarray a[lo .. hi] using 3-way partitioning private static void sort(Comparable[] a, int lo, int hi) { if (hi <= lo) return; int lt = lo, gt = hi; //less than greater than Comparable v = a[lo]; //最左作为枢轴 int i = lo; //i是计数下标,从左到右移动的游标 while (i <= gt) { gt是没有考察过的,所以边界是有=的。循环确保[lo,lt-1]都小于枢轴,[gt+1,hi]都大于枢轴。 int cmp = a[i].compareTo(v); if (cmp < 0) exch(a, lt++, i++); else if (cmp > 0) exch(a, i, gt--); //继续考察从后边交换出来的,所以i不++ else i++; } // a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]. 最终lt和gt位置上都是和枢轴相等的数 sort(a, lo, lt-1); sort(a, gt+1, hi); assert isSorted(a, lo, hi); } /*作者:dontbeatmycat 链接:https://www.zhihu.com/question/23171968/answer/23804982 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。*/
快速排序
最新推荐文章于 2024-08-12 19:18:27 发布