关于快速排序的深入分析

快排几乎是每次面试必考的问题,实际中应用也很广泛,但是我发现自己看完之后,当时觉得理解了,但是过段时间就忘了。。所以下面就对它进行一次深刻细致的分析,能够随时想起来,至少把它的思路印在脑子里。


  1. 虽然快排的最坏情况时间复杂度为Ө(n^2);但期望时间复杂度为Ө(nlgn),且隐含的常数因子非常小;快排的另一大优势在于它可以原址排序,这意味着空间复杂度是Ө(1)。
  2. 快排同样采用了分治思想,即把原数组分成两个子数组后,再在子数组中分别递归调用本身。
    但是快排的特色在于分组的方式:每次分组时将产生一个界标,界标左侧是小于等于自己的元素(没有排序,只是混乱的罗列;但也不必排序,只需要他们小于等于界标就好);同时,界标右侧全是大于自己的元素。界标确定后,将不再参与下一次的排序,因为从某种意义上,它在整个原数组中的位置已经是对的了。递归调用分别在界标左右两侧的子数组中进行。本质上,快排就是不断产生界标的过程。
    非平凡的,快排每次启动时,把目标(子)数组的最后一个元素当做界标,把它置换到数组合适的位置上。之所以选最后一个,可能是出于操作上的方便,事实上,选取数组中任何一个元素都是可以的,没有太大区别,只是细节上操作不太方便而已。
    我想强调的是,当一个数组给定时,只要确定排序方式(从大到小或反之),那么末尾元素在数组中的位置就是确定的(这好像是一句废话),这是因为:数组中所有大于它的元素的个数已经确定了,同时小于等于它的元素个数也是确定的。我们根据这些元素的个数就可以确定末尾元素的位置。每次分组时,快排算法只关心在界标元素左右的元素的个数,并不关心它们是否排好序。我个人认为,这才是快排的核心思想。
  3. 伪代码

    Quicksort(A, p, r)
     if(p < r)
         q = Partition(A, p, r)
         Quicksort(A, p, q-1)
         Quicksort(A, q+1, r)
    
    Partition(A, p, r)
        temp = A[r]
        i = p-1
        for j = p to r-1
            if A[j] <= temp
            i = i+1
            exchange A[i] with A[j]
       exchange A[i+1] with A[r]
      return i+1
    

    可以看出,快排的关键在于分组函数Partition(),它的任务不仅是找到末尾元素pivot在数组中的正确位置将其返回,同时还要在返回之前将处于该位置的元素和pivot对调。第一个任务显然要更费力一些:因为数组中到底有多少个元素小于等于pivot,并不是一眼就能看出来的,所以必须要经过一次遍历才能知道。遍历的目标是前n-1个元素。
    在遍历过程中,Partition设置了两个指标:j,代表当前正在遍历的元素的下标;而i,代表小于等于pivot的子数组的末尾元素的下标(某种程度上就是这个子数组的长度)。
    只要目标数组中有小于等于pivot的元素,i的值就会不断增长;当然i增长的同时,A[i+1]还会和被选中元素A[j](小于等于pivot)置换。到最后是pivot本身的一次置换。

暂时就这么多吧,我觉得已经很清楚了。

PS:目前已入职宜信近两个月了,这期间一直在恶补java web开发的相关知识,包括各种前后端框架,根本没有时间写博客。。但随着我逐渐熟悉业务,博客更新会慢慢恢复的。
宜信现在换了一个CTO,裁了三分之二的人,并在大规模补充新鲜血液,欢迎各位有志于互联网金融的小伙伴加入,在这里绝对有你发挥的舞台哦~


<–2017.8.4更新 –>
贴一段java代码实现:

public class QuickSort {
    public static void main(String[] args) {
        ConsoleHandler handler = new ConsoleHandler();
        handler.setLevel(Level.FINE);


        Logger logger =  Logger.getLogger("W");
        logger.setLevel(Level.ALL);
        logger.addHandler(handler);
        logger.log(Level.INFO, "hehe");
    }

    public static void qsort(int[] arr, int start, int end) {
        if (start < end) {
            int mid = sort(arr, start, end);
            qsort(arr, start, mid - 1);
            qsort(arr, mid + 1, end);
        }
    }

    public static int sort(int[] arr, int start, int end) {
        int i = start - 1;
        for (int j = start; j < end; j++) {
            if (arr[j] < arr[end]) {
                i++;
                swap(arr, i, j);
            }
        }
        swap(arr, i + 1, end);
        return i + 1;
    }

    public static void swap(int[] arr, int start, int end) {
        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值