算法-快速排序算法

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u012534831/article/details/75000212

快速排序算法是分治算法技术的一个实例,也称为分区交换排序。快速排序采用递归调用对元素进行排序,是基于比较的排序算法中的一个著名算法,也是面试常考的一个算法。

本文主要思想借鉴于这篇文章:

白话经典算法系列之六 快速排序 快速搞定
http://blog.csdn.net/morewindows/article/details/6684558

时间复杂度:

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)

性能:

假设快速排序的复杂度为T(n) 且所有元素都不相同。T(n) 取决于两个子问题的规模,而规模又取决于中枢点。

  • 最好情况:每个划分把数组分成相等的两部分。时间复杂度为(nlogn);
  • 最坏情况:每个划分把数组分成不相等的两部分。时间复杂度为(n^2),最坏情况发生在序列已经排序且选择最后一个元素作为中枢点。若初始序列有序则会退化为选择排序。
  • 平均情况下的时间复杂度与最好情况一样也为(nlogn);

基本思想:

书上说该算法主要由以下四步组成:

  1. 如果数组中仅有一个元素或者没有元素需要排序,则返回。
  2. 选择数组中的一个元素作为中枢点(通常选择数组最左边的元素)。
  3. 把数组分成两部分,一部分元素大于中枢点,一部分元素小于中枢点。
  4. 对两部分数组递归调用该算法。

借鉴参考文章,在这里我们巧妙的把它翻译为这样一种思想:

挖坑填数、分而治之。

操作过程如下:

假设有如下数组,且拟定有一个数组 A[4,6,2,9,1,17] ,下标为index,和两个指针 left 和 right,和一个保存中枢点的变量 temp。

初始时,取第一个元素为中枢点,此时:left = 0; right = 5; temp = A[left] = 4;
这里写图片描述
由于已经将A[0]中的数保存到temp中,可以理解成在数组A[0]上挖了个坑,可以将其它数据填充到这来。

1、首先从右向左,寻找第一个比temp小的数,准备填充到A[0]的位置上。
当right=4时满足情况,于是将A[4] 挖走,填充到上一个坑A[0]处,并且left++;此时:left=1;right=4;temp=4;形成新坑A[4]。
这里写图片描述
2、开始从左往右,寻找第一个比temp大的数,准备填充到A[4]的位置上。当left=1时,便满足条件,于是将A[1]挖走,填到上一个坑A[4]处,并且right–;此时:left=1;right=3;temp=4;形成新坑A[1];
这里写图片描述
3、开始从右往左,寻找第二个比temp小的数,准备填充到A[1]的位置上。
当right=2时满足情况,于是将A[2] 挖走,填充到上一个坑A[1]处,并且left++;此时:left=2;right=2;temp=4;形成新坑A[2]。
这里写图片描述
4、此时left=right,则第一趟排序结束,将temp填充到中枢点index=2的位置上,可以看到,4的左边都比4小,右边都比4大。
这里写图片描述
5、得到新的中枢点后,递归对左边和右边重复上述过程即可完成整个排序。

对挖坑填数进行总结(引用自上述参考文章):

1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。
2.j–由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。

算法实现(java):

    public static void sort(int[] A,int l,int r){
        int left=l;
        int right=r;
        // index为下标,temp用来保存中枢位置的值,无关数组A,只是一个临时变量而已
        int index,temp;

        if(A.length==0 || A.length==1){return;}
        if(left>right){return;}
        //选取中枢,通常选择最左边的元素
        temp=A[left];   
        while(left < right){
        //先从右向左找不大于temp的元素
         while(left < right && A[right] > temp){  
             right--;
         }  
         if(left < right){
            //挖right,填left
            A[left]=A[right];  
            //填完之后,left右移一位        
            left++;                   
         }
         //再从左向右找不小于temp的元素来填上一个坑
         while(left < right && A[left] <= temp){  
             left++;
         }
         if(left < right){
             //挖left,填上一次的right
             A[right]=A[left];   
             //填完之后,right左移一位        
             right--;                   
         }
        }
        //走到这一步,必然left==right,得到中枢位置,将temp插入中枢位置
        A[left]=temp;   
        //下标index                       
        index=left;                 
        //执行完一次排序操作,再对左边和右边分别递归
        sort(A,l,index-1);
        sort(A,index+1,r);
    }

测试程序:

public static void  main(String[] arg){
        // TODO Auto-generated method stub
        int[] A=new int[]{1,4,2,8,5,9,477,25,1};

        long time=System.nanoTime();
        sort(A, 0, A.length-1);
        System.out.println("耗时:"System.nanoTime()-time);
        for (int i : A) {
            System.out.println(i);  
        }
    }

打印内容如下:

耗时:9659
1
1
2
4
5
8
9
25
477

csdn地址:http://blog.csdn.net/u012534831
github地址:https://github.com/qht1003077897
个人博客地址:https://qht1003077897.github.io
QQ:1003077897

猜你喜欢

转载自blog.csdn.net/u012534831/article/details/75000212