快速排序详解(附python实现)

快速排序(Quicksort)是对冒泡排序的一种改进,由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程递归进行,以此使整个数据变成有序序列

快速排序的核心思想是划分(partion)和分治(divide and conquer)。先讲一下划分问题,给定一个无序数组,选定其中一个元素为基准元素,划分问题的目的是把基准元素放到数组中合适的位置,使其前面的元素都小于基准元素,后面的元素都大于这个基准元素。例如数组{ 5、3、7、4、6、9 },如果我们选取5为基准元素,最后经过划分得到数组{ 4、3、5、7、6、9 },可以看到5之前的元素都比它小,5后面的元素都比它大。注意基准元素可以选取数组第一个元素或者最后一个元素,或者每次随机选取。每经过一次划分,就有一个元素被划分到了正确的位置,反复调用划分算法便可以把数组变成有序数组。

划分问题有两种常见的算法:单指针和双指针法,时间复杂度都是 O(n)。《算法导论》中给出了单指针的划分算法,个人感觉没有双指针的容易理解(emmm,可能是我太弱了),下面给出算法导论中给出的单指针算法的伪代码,不再细说。

  • 1. i← low
  • 2. x←A[low] //每次选定第一个元素为基准元素
  • 3. for j← low + 1 to high
  • 4.       if A[ j ] <= x then
  • 5.            i← i + 1
  • 6.            if i ≠j then 互换A[ i ] A [ j ]
  • 7.       end if
  • 8. end for
  • 9. 互换A[low] A[i]
  • 10. w← i
  • 11. return A w

双指针算法比较容易理解,指定数组的头指针尾指针,头指针从第一个元素往后遍历,遇到比基准元素大的元素停止。尾指针从最后一个元素往前遍历,遇到比基准元素小的元素停止。然后交换头指针和尾指针指向的元素。继续上述过程,直到头指针与尾指针重合,最后调换基准元素和头指针(或者尾指针,视情况而定)的位置。图示如下(选取最后一个元素为基准元素):

上图最后得到的序列,把5和9调换位置后,就得到了一个划分序列  {4、1、3、0、2、5、8、6、7、9}。下面是双指针算法的伪代码:

  • 1. key←A[low] //每次选定第一个元素为基准元素
  • 2. loc← low   //记录基准元素的位置
  • 3. while low <= high
  • 4.       while low <= high and A[low] < key
  • 5.              low ← low + 1
  • 6.       end while
  • 7.       while low <= high and A[high] > key
  • 8.             high ← high – 1
  • 9.       end while
  • 10.     if low < high then 互换A[low]A[high]
  • 11. end while
  • 12. 互换A[high] A[low]
  • 13. return A high

讲完划分算法后,我们来看分治。分治,即分而治之。将原始问题分解为若干子问题,在逐个解决各个子问题的基础上,得到原始问题的解,一般用递归实现。快速排序实现过程中,每次我们使用一次划分算法可以将基准元素放到正确的位置上,例如{4,3,5,9,7},5是基准元素且已经被放到了正确的位置上,将数组分为前后两部分,然后我们只需对{4、3}和{9、7}进行排序即可,这个过程用递归很容易实现。上述便体现了分治思想,实现如下:

def quicksort(array, low, high):
    if low < high:
        w = double_pointer_partion(array, low, high)  
        quicksort(array, low, w)  
        quicksort(array, w + 1, high)

完整代码python实现如下:

def one_pointer_partion(array, low, high):
    i = low
    x = array[low]  # 第一个元素为基准元素
    for j in range(low + 1, high):
        if array[j] <= x:
            i = i + 1
            if i != j:
                array[i], array[j] = array[j], array[i]
    array[low], array[i] = array[i], array[low]
    w = i
    return w


def double_pointer_partion(array, low, high):
    key = array[low]  # 第一个元素为基准元素
    high -= 1  # 数组索引从0开始,因此指向最后一个元素的指针应该是high-1
    loc = low
    while low <= high:
        while low <= high and array[low] <= key:  #找到比key大的元素
            low += 1
        while low <= high and array[high] >= key: #找到比key小的元素
            high -= 1
        if low < high:
            array[low], array[high] = array[high], array[low] #交换两个元素的位置
    array[loc], array[high] = array[high], array[loc]
    return high


def quicksort(array, low, high):
    if low < high:
        w = double_pointer_partion(array, low, high)  # 或者是w = one_pointer_partion(array, low, high)
        quicksort(array, low, w)  # 基准元素已经排列好,放在了数组的正确位置,只需用递归把基准元素前面的序列和后面的序列排列好即可。
        quicksort(array, w + 1, high)


if __name__ == '__main__':
    a = [5, 3, 6, 1, 7, 4, 8]
    quicksort(a, 0, len(a))
    print(a)

      

猜你喜欢

转载自blog.csdn.net/zhang123454lulu/article/details/83542316