对于包含
n
n
n个数的输入数组来说,快速排序是一种最坏情况时间复杂度为
Θ
(
n
2
)
\Theta(n^2)
Θ(n2)的排序算法。虽然最坏情况时间复杂度很差,但是快速排序通常是实际排序应用中最好的选择,因为它的平均性能非常好:它的期望时间复杂度是
O
(
n
lg
?
n
)
O(n\lg n)
O(nlgn),而且
O
(
n
lg
?
n
)
O(n\lg n)
O(nlgn)中隐含的常数因子非常小。另外,它还能够进行原址排序,甚至在虚存环境中也能很好地工作。
《排序算法(七):快速排序-[基础知识]》将描述快速排序算法及它的一个重要的划分子程序。因为快速排序的运行情况比较复杂,在《排序算法(七):快速排序-[快速排序的性能]》中,我们先对其性能进行一个直观的讨论,在《快速排序》系列文章的最后会给出一个准确的分析。在《排序算法(七):快速排序-[快速排序的随机化版本]》中,我们会介绍一个基于随机抽样的快速排序算法。这一算法的期望时间复杂度较好,而且没有什么特殊的输入会导致最坏情况的发生。《排序算法(七):快速排序-[分析快速排序]》对这一随机算法的分析表明,其最坏情况时间复杂度是
Θ
(
n
2
)
\Theta(n^2)
Θ(n2),在元素互异的情况下,期望时间复杂度
O
(
n
lg
?
n
)
O(n\lg n)
O(nlgn)。
与归并排序一样,快速排序也使用了《分治策略(一):基础知识》介绍的分治思想。下面是对一个典型的子数组A【p门进行快速排序的三步分治过程:
- 分解:数组
A
[
p
?
r
]
A[p\cdots r]
A[p?r]被划分为两个(可能为空)子数组
A
[
p
?
q
?
1
]
A[p\cdots q-1]
A[p?q?1]和
A
[
q
+
1
?
r
]
A[q+1\cdots r]
A[q+1?r],使得
A
[
p
?
q
?
1
]
A[p\cdots q-1]
A[p?q?1]中的每一个元素都小于等于
A
[
q
]
A[q]
A[q],而
A
[
q
]
A[q]
A[q]也小于等于
A
[
q
+
1
?
r
]
A[q+1\cdots r]
A[q+1?r]中的每个元素。其中,计算下标
q
q
q也是划分过程的一部分。
- 解决:通过递归调用快速排序,对子数组
A
[
p
?
q
?
1
]
A[p\cdots q-1]
A[p?q?1]和
A
[
q
+
1
?
r
]
A[q+1\cdots r]
A[q+1?r]进行排序。
- 合并:因为子数组都是原址排序的,所以不需要合并操作:数组
A
[
p
?
r
]
A[p\cdots r]
A[p?r]已经有序。
下面的程序实现快速排序:
def partition(arr,low,high):
i = low-1
pivot = arr[high]
for j in range(low, high):
if arr[j] <= pivot:
i = i+1
arr[i],arr[j] = arr[j],arr[i]
arr[i+1],arr[high] = arr[high],arr[i+1]
return i+1
def quick_sort(arr,low,high):
if low < high:
pi = partition(arr,low,high)
quickSort(arr, low, pi-1)
quickSort(arr, pi+1, high)
最后,我们用动图演示一下插入排序的全过程:
|