希尔排序与快速排序

上节讲的归并排序运行速度比简单排序块,但是它需要的空间是原始数组空间的两倍;通常这是一个严重的缺点
        希尔排序,希尔排序的时间复杂度是O(N*(logN)^2)。希尔排序是基于插入排序的。希尔排序又叫缩小增量排序,它是基于插入排序的增强版。
        基本思想:把记录按步长进行分组,对每组记录采用直接插入的方法进行排序。随着步长的缩小,所分成的组包含的记录就越来越多,当步长的值减小到1时
,整个数据合成一组,构成一组有序的记录,则完成排序。
        过程如下图所示:
http://images2015.cnblogs.com/blog/318837/201604/318837-20160422102024757-37862627.png
        具体的执行步骤是:
        1,在第一趟排序过程中,我们不妨设gap1=N/2=5,即为相隔距离为5的元素组成一组,可以分为5组。接下来,按照直接插入排序的方法对每个组进行排序。
        2,第二趟排序中,我们把上次的gap缩小一半,即为gap2=gap1/2=2(取整数)。这样每相隔距离为2的元素组成一组,可以分为2组。按照直接插入排序的方法
对每个组进行排序。
        3,在第三趟排序中,再次把gap缩小一半,即为gap3=gap2/2=1。这样相隔距离为1的元素组成一组,即只有一组。按照直接插入排序的算法对每个组进行排序。此时排序结束。
        注意希尔排序是不稳定的排序。
        核心代码:
                public void shellsort(int[] list){
                        int gap=list.length/2;
                        while(gap>=1){
                                for(int i=gap;i<list.length;i++){
                                        int j=i-gap;
                                        int temp=list[i];
                                        if(j>=0&&list[j]>temp){
                                                list[i]=list[j];
                                                list[j]=temp;
                                        }
                                }
                                gap=gap/2;
                        }
                        printAll(list);
                } 

毫无疑问,快速排序是最流行的排序算法,大多是情况下,快速排序都是最快的,执行时间是O(N*logN)级别。它是一种既不浪费空间也不浪费时间的排序算法。
        方法其实很简单:给一个序列“6 1 2 7 9 3 4 5 10 8”两端开始“探测”。以第一个数作为基准数(6),先从右向左找一个小于6的数,再从左向右找一个大于6的数,然后交换他们。这里可以用两个变量i和j,分别指向最左边和最右边。我们为这两个变量起一个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向>序列的最左边(即为i=0),指向数字6.让哨兵j指向序列的最右边(即为i=9),指向数字8.如图:

        首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,这一点非常重要。哨兵j一步一步的向左挪动(即为j--),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即为i++),直到找到一个大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。如图:


        现在交换哨兵i和哨兵j所指向的元素的值,交换后的序列如下:
        6 1 2 5 9 3 4 7 10 8
        到此,第一次交换结束。接下来开始哨兵j继续向左挪动(注意,每次必须数哨兵j先出发)。他发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动,他发现了9(比基准数6大,满足要求)之后停了下来。如下图:


        此时再次进行交换,交换之后的序列如下:
        6 1 2 5 4 3 9 7 10 8
        第二次交换结束后,“探测”继续。哨兵j继续向左挪动,他发现了3(比基准数6小,满足要求)之后停了下来。哨兵i继续向右挪动,糟糕!此时哨兵i和哨兵
j相遇,哨兵i和哨兵j都走到了3面前。说明此时“探测”结束。我们将基准数6和3进行交换。如下图:


        交换后的序列如下:
        3 1 2 5 4 6 9 7 10 8

        此时,第一轮“探测结束”。此时以基准数6为分界点,6左边的数小雨等于6,6右边的数大于等于6.
        将以6为分界点的两个子序列:3 1 2 5 4和9 7 10 8再次进行快速排序.整个图的处理过程如下图所示:

        注意:快速排序的最差时间复杂度为O(N^2),它的平均复杂度为O(NlogN),它是基于“二分”的思想。

        代码实现:

        public void quicksort(int[] n,int left,int right){
                int dp;
                if(left<=right){
                        dp=partion(n,left,right);
                        quicksort(n,left,dp-1);
                        quicksort(n,dp+1,right);
                }
        }
        public void partion(int[] n,int left,int right){
                int pro=n[left];
                int start=left;
                int temp=0;
                while(left<right){
                        while(left<right&&n[right]>=pro){
                                right--;
                        }
                        if(left<right){
                                temp=n[right];
                        }
                        while(left<right&&n[left]<=pro){
                                left++;
                        }
                        if(left<right){
                                n[right--]=n[left]
                                n[left++]=temp
                        }
                }
                n[start]=n[left];
                n[left]=pro;
                return left;
        }
转载自:静默虚空 坐在马桶上看算法:快速排序
阅读更多

没有更多推荐了,返回首页