快速排序(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,可能是我太弱了),下面给出算法导论中给出的单指针算法的伪代码,不再细说。
双指针算法比较容易理解,指定数组的头指针和尾指针,头指针从第一个元素往后遍历,遇到比基准元素大的元素停止。尾指针从最后一个元素往前遍历,遇到比基准元素小的元素停止。然后交换头指针和尾指针指向的元素。继续上述过程,直到头指针与尾指针重合,最后调换基准元素和头指针(或者尾指针,视情况而定)的位置。图示如下(选取最后一个元素为基准元素):
上图最后得到的序列,把5和9调换位置后,就得到了一个划分序列 {4、1、3、0、2、5、8、6、7、9}。下面是双指针算法的伪代码:
讲完划分算法后,我们来看分治。分治,即分而治之。将原始问题分解为若干子问题,在逐个解决各个子问题的基础上,得到原始问题的解,一般用递归实现。快速排序实现过程中,每次我们使用一次划分算法可以将基准元素放到正确的位置上,例如{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)