快排是经典的二分法思想的排序方法, 它是不稳定的(即a0, a1的值均为1, 但是排序后可能a1在a0前面, 不保证相对顺序不变).
-
输入: 数组A, 长度为L.
-
输出: 排序后的数组A.
-
基本思想:
- 选择"基准元": 从输入数组中随机取一个数字作为基准(pivot);
- 划分: 将所有小于pivot的数字放到pivot左边, 记为子数组A_left,
所有大于pivot的数字放到pivot右边, 记为A_right; - 递归: 对子数组A_left和A_right递归的调用步骤①和步骤②.
经典的快速排序划分方法和荷兰国旗法的区别在于:
- 经典方法是返回一个位置索引position, 将数组划分为两个部分:
A [ p o s i t i o n ] = = p i v o t ; A l e f t = { A [ i ] ∣ A [ i ] < = p i v o t } ; A r i g h t = { A [ j ] ∣ A [ j ] > p i v o t } ; A[position] == pivot; \\ A_{left} = \{ A[i] | A[i] <= pivot \}; \\ A_{right} = \{ A[j] | A[j] > pivot \}; A[position]==pivot;Aleft={A[i]∣A[i]<=pivot};Aright={A[j]∣A[j]>pivot}; - 而荷兰国旗法返回的是一个开区间(left, right), 将数组划分为三个部分:
A l e f t = { A [ i ] ∣ A [ i ] < p i v o t } ; A m i d = { A [ k ] ∣ A [ k ] = = p i v o t } ; A r i g h t = { A [ j ] ∣ A [ j ] > p i v o t } ; A_{left} = \{A[i] | A[i] < pivot \}; \\ A_{mid} = \{A[k] | A[k] == pivot\}; \\ A_{right} = \{A[j] | A[j] > pivot\}; Aleft={A[i]∣A[i]<pivot};Amid={A[k]∣A[k]==pivot};Aright={A[j]∣A[j]>pivot};
由此可以看出, 荷兰国旗法的效率更高, 每一次划分, 如果选择的基准元有重复值的话, 会一次性将重复值放到中间, 不会再参与下一次划分.
荷兰国旗法的关键在于使用两个标记left, 和right, 在遍历元素的过程中, 保证left左边的元素都小于pivot, right右边的元素都大于pivot.
代码实现(python, 荷兰国旗法)
def swap(A, i, j):
tmp = A[i]
A[i] = A[j]
A[j] = tmp
def partition(A, start, end):
left = start - 1
right = end
pivot = A[end-1]
k = start
while k < right:
if A[k] == pivot:
k += 1
elif A[k] < pivot: # 将碰到的<pivot的值放到left控制的左半部分
left += 1
swap(A, k, left)
k += 1
else: # 将碰到的>pivot的值放到right控制的右半部分
right -= 1
swap(A, k, right)
return left, right
def QSort(A, start, end):
if start < end:
left, right = partition(A, start, end)
QSort(A, start, left+1)
QSort(A, right, end)```