美文网首页
对快排的一点认识

对快排的一点认识

作者: 克罗地亚催眠曲 | 来源:发表于2017-11-09 10:50 被阅读11次

    最近需要用到快排,所以想用手写一下快排的代码,快排的原理是相当清楚的,每次找到一个pivot(基准值),小于等于该值的放到数组的左半边,大于该值的放到数组的右半边,这样就能把原数组一分为二,减小了数组的规模,再对子数组做相同的处理,直到子问题的数组规模变为单个数组时,该数组就已经是有序的了。说写就写,下面是我一开始写出来的代码。

    import numpy as np
    import sys
    
    def QuickSort(a, ll, rr):
        if ll >= rr: return
        pos = did(a, ll, rr)
        QuickSort(a, ll, pos-1)
        QuickSort(a, pos+1, rr)
    
    def did(a, ll, rr):
        piv = a[ll]
        t  = ll
        while ll < rr:
            while ll < rr and a[ll] <= piv: ll += 1
            while ll < rr and a[rr] > piv: rr -= 1
            a[ll], a[rr] = a[rr], a[ll]
        a[ll], a[t] = a[t], a[ll]
        return ll
    
    def test():
        for i in range(5):
            lst = np.random.randint(0, 100, 10)
            print(lst)
            QuickSort(lst, 0, len(lst)-1)
            print(lst)
    
    if __name__ == '__main__':
        test()
    
    

    一开始运行的结果如下图所示


    quicksort-1.png

    仔细观察发现结果是错误的。于是开始找问题究竟出在哪了。
    找啊找,找啊找,就是想不出来。最后手工模拟了一遍之后发现问题的根源。
    导致发生错误的地方就是下面两行代码的顺序问题

    while ll < rr and a[ll] <= piv: ll += 1
    while ll < rr and a[rr] > piv: rr -= 1
    

    调换一下这两行的顺序之后,运行结果如下图,看来结果是正确的。


    quicksort-2.png

    为什么这两行代码的顺序会影响程序的正确性呢?

    手动模拟一下快排就会发现,我们取最左边的数组元素作为pivot,并且在每一轮迭代后,把a[t]和a[ll]的值进行交换,这里t值为原始的ll。每一轮迭代结束的条件是ll == rr,而我们交换a[t]和a[ll]的必要条件是a[ll] <= a[t],按照最原始的写法,如果第一个while循环结束时满足ll == rr,那么第二个循环不会执行,此时不能确保a[ll] <= a[t]。调换两行while代码的顺序后,如果第一个while循环结束的时ll==rr,则ll的值为上一轮循环结束时的值,满足a[ll] <= a[t],如果第一个while循环结束时的条件式为a[rr] <= piv,则第二个while循环可以执行,不会出现错误。

    我所理解的就是这样的。所以如果我们每次把数组区间的最后一个元素作为pivot的话,应该先执行检测a[ll]值的那个while循环。

    以前没有注意到这些细节问题,这次踩了个坑,所以学习算法只了解了原理是不够的,还要多动手实现一些算法,才能真正掌握。

    相关文章

      网友评论

          本文标题:对快排的一点认识

          本文链接:https://www.haomeiwen.com/subject/qcrpmxtx.html