排序算法-快速排序(Quick Sort)

Posted 詩和遠方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法-快速排序(Quick Sort)相关的知识,希望对你有一定的参考价值。

关于Quick Sort的介绍网上资料非常多,本文仅是我重新学习此算法时的笔记,如有纰漏,还请网友指出。
快排算法利用分治法(divide and conquer)思想,每次将数组中的一个基准元素(pivot)放在其应该在的位置,所有比基准小的数放在其左边,比基准大的元素放在其右边。然后将左右两个子序列进行递归处理。算法的关键是用基准将数组拆分成两个子序列,我们暂且将这个过程称之为partition,具体有几种不同的实现方法。这里均用python实现。

partition函数

实现一

def partition(ls, left, right):
    pivot = ls[left]  # first element as pivot
    i, j = left, right
    while i < j:      # check j first, the order is important
        while ls[j] >= pivot and i < j:
            j -= 1
        while ls[i] <= pivot and i < j:
            i += 1
        if i < j:
            ls[i], ls[j] = ls[j], ls[i]
    ls[i], ls[left] = ls[left], ls[i]
    return i

以第一个元素作为基准,维护左右两个指针,从后往前遍历数组S,找到第一个比基准数小的元素,从前往后遍历数组,找到第一个比基准数小的元素,交换这两个数,重复以上过程,直到指针下标相等,最后再将基准元素与指针所在元素交换。
每一次交换如下:

实现二

def partition(ls, left, right):
    pivot = ls[left]  # first element as pivot, treat ls[left] as an hole
    i, j = left, right
    while i < j:
        while ls[j] >= pivot and i < j:
            j -= 1
        if i < j:    # find ls[j] less than pivot, put it to the hole ls[i], then move i forward 
            ls[i] = ls[j]
            i += 1
        while ls[i] <= pivot and i < j:  
            i += 1
        if i < j:   # find ls[i] greater than pivot, put it to the hole ls[j], then move j backward
            ls[j] = ls[i]
            j -= 1
    ls[i] = pivot   # if i == j, put pivot back to hole
    return i

以上代码看上去有点奇怪,每次只是赋值,并没有像第一种方法一样进行交换操作,实际可以这样理解:移出pivot,ls[0]的值已重复多余,就当它是一个空白/洞,等着别的值来覆盖/填充,然后右边找到第一个比pivot小的元素ls[j],将它填充到ls[0],此时轮到ls[j]的值重复多余……,最后用pivot的值覆盖ls[i]。
第一轮交换:
实现三

def partition(ls, left, right):
    pivot = ls[left]  # first element as pivot
    i, j = left, right
    while i < j:      # check j first, the order is important
        while ls[j] >= pivot and i < j:
            j -= 1
        if i < j:     # find a[j] < pivot, swap them, then move i forward
            ls[i], ls[j] = ls[j], ls[i]
            i += 1
        while ls[i] <= pivot and i < j:
            i += 1
        if i < j:     # find a[i] > pivot, swap them, then move j backward
            ls[i], ls[j] = ls[j], ls[i]
            j -= 1
    return i

以上代码与实现二有点相似,唯一的差别在于每次都交换,所以不用像实现二一样做最后一次将pivot归位的操作。
第一轮交换:

主函数

def quick_sort(ls, left, right):
    if left < right:
        i = partition(ls, left, right)
        quick_sort(ls, left, i - 1)
        quick_sort(ls, i + 1, right)

if __name__ == "__main__":
    ls = [85, 24, 63, 45, 17, 31, 96, 50, 85]
    quick_sort(ls, 0, len(ls) - 1)
    print(ls)

代码关键点说明

  • partition函数是核心,其中第一版最简洁,交换次数也最少
  • 其实以上三种实现,差别并不大,这里啰嗦的将他们都列出来,只是希望通过比较实现细节,从而能更深刻地理解快速排序算法
  • 以上算法均以第一个元素作为pivot,需要先移动右边的指针,因为最后一次交换是pivot与ls[i]的交换,而pivot位于最左边,所以ls[i]不能大于pivot,否则交换之后ls[i]在最左边,却比pivot大,这显然是不正确的。反之如果算法将最后一个元素作为pivot,则需要先移动左边的指针。

以上是关于排序算法-快速排序(Quick Sort)的主要内容,如果未能解决你的问题,请参考以下文章

python 学习笔记 -- 数据结构与算法 快速排序 Quick Sort

交换排序—快速排序(Quick Sort)

快速排序算法回顾 --冒泡排序Bubble Sort和快速排序Quick Sort(Python实现)

算法之排序算法 -- 快速排序 Quick sort

算法分析 - 快速排序QUICK-SORT

快速排序 Quick Sort