快速排序

  1. 快速排序,先理解其思想,见维基百科--快速排序。找几个例子试了一下。基本是:给定一个数组,编号从0到n,找其中一个数字作为枢轴,比该枢轴小的数都放在左边,比该枢轴大的都放在右边。之后再分而治之,分别对左边和右边的数组进行排序。
  2. 转化为代码。
    #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>
  3. 以上是第一次编写出来的程序,不能确保每次排序之后,有一个元素被放到正确的位置上,如 5 6 7 1 2中排序之后是 1 2 7 5 6.
  4. 难点(个人感觉):单趟排序中,对于数组中出现相同元素的处理,如5 5 3 2 6.尤其是待比较的元素与枢轴大小相同时;边界问题,<= 还是< ;循环什么时候结束。结束的边界条件;整个大的排序何时结束,结束条件。
  5.  在这次编程中,如何验证程序的正确性的思考。大量的测试用例。形式化的证明。有对此有研究的朋友欢迎赐教
  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
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。*/

     

  7. Jon Bently在Beautiful Code上的代码。l是low,最左的序号,u是upper,最右的序号。核心:循环不变式,数学归纳的思想:确保[l+1,m]都是比枢轴小的数字。在之后发现的比枢轴小的数都交换到m+1的位置。始终确保[l+1,m]是比枢轴小的数字,之后交换枢轴和m位置上的数,确保了每次排序都将一个数字放在了正确的位置上。

  8. 这个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
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。*/

     

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值