问题
用Quick Sort对长度为n n n 的无序序列s s s 从小到大(升序)排序。
解法
在长度为n n n 的序列s = [ x 0 , x 1 , … , x n − 1 ] s = [x_0, x_1, \dots, x_{n-1}] s = [ x 0 , x 1 , … , x n − 1 ] 选取最左边元素x 0 x_0 x 0 作为p i v o t pivot p i v o t ,然后将剩余部分分为两个部分l e f t = [ x 1 , … , x k ] left = [x_1, \dots, x_k] l e f t = [ x 1 , … , x k ] 和r i g h t = [ x k + 1 , … , x n − 1 ] right = [x_{k+1}, \dots, x_{n-1}] r i g h t = [ x k + 1 , … , x n − 1 ] (其中1 ≤ k ≤ n − 1 1 \le k \le n-1 1 ≤ k ≤ n − 1 ),满足l e f t left l e f t 所有元素都位于p i v o t pivot p i v o t 左边,小于等于p i v o t pivot p i v o t ,r i g h t right r i g h t 所有元素都位于p i v o t pivot p i v o t 右边,大于等于p i v o t pivot p i v o t ,而l e f t , r i g h t left, right l e f t , r i g h t 内部并不必须是有序的。如图:
将s = [ x 0 , … , x n − 1 ] s = [x_0, \dots, x_{n-1}] s = [ x 0 , … , x n − 1 ] 中任意x < p i v o t x \lt pivot x < p i v o t 移动到其左边,任意x > p i v o t x \gt pivot x > p i v o t 移动到其右边。需要如下操作:
Copy function Partition(s, low, high):
let pivot = s[low]
while low < high
while low < high and s[high] >= pivot
high--
s[low] = s[high]
while low < high and s[low] <= pivot
low++
s[high] = s[low]
s[low] = pivot
return low
(1) Partition函数第2行:令s s s 第一个元素s [ l o w ] s[low] s [ l o w ] 作为p i v o t pivot p i v o t ;
(2) Partition函数第3-9行:轮流从s s s 最右边选出第一个x < p i v o t x \lt pivot x < p i v o t 移动到其左边,从s s s 最左边选出第一个x > p i v o t x \gt pivot x > p i v o t 移动到其右边,直到l o w ≥ h i g h low \ge high l o w ≥ hi g h ;
(3) Partition函数第10-11行:经过移动之后l o w low l o w 的位置即为最终p i v o t pivot p i v o t 的位置,将该位置返回给函数调用者;
上述操作的示例如图:
设p i v o t = s [ 0 ] = 45 , l o w = 0 , h i g h = n − 1 pivot = s[0] = 45, low = 0, high = n-1 p i v o t = s [ 0 ] = 45 , l o w = 0 , hi g h = n − 1 。从h i g h high hi g h 向左搜索第一个s [ h i g h ] = 29 < p i v o t = 45 s[high] = 29 \lt pivot = 45 s [ hi g h ] = 29 < p i v o t = 45 ,令s [ l o w ] = s [ h i g h ] s[low] = s[high] s [ l o w ] = s [ hi g h ] 。
再从l o w low l o w 开始向右搜索第一个s [ l o w ] = 90 > p i v o t = 45 s[low] = 90 \gt pivot = 45 s [ l o w ] = 90 > p i v o t = 45 ,令s [ h i g h ] = s [ l o w ] s[high] = s[low] s [ hi g h ] = s [ l o w ] 。
再从h i g h high hi g h 向左搜索第一个s [ h i g h ] = 13 < p i v o t = 45 s[high] = 13 \lt pivot = 45 s [ hi g h ] = 13 < p i v o t = 45 ,令s [ l o w ] = s [ h i g h ] s[low] = s[high] s [ l o w ] = s [ hi g h ] 。
上述操作直到l o w ≥ h i g h low \ge high l o w ≥ hi g h 停止,此时l o w low l o w 的位置即为p i v o t pivot p i v o t 的位置,令s [ l o w ] = p i v o t s[low] = pivot s [ l o w ] = p i v o t 就完成了本轮操作。
递归的对l e f t left l e f t 和r i g h t right r i g h t 进行该操作,即可完成整个数组排序:
Copy function QuickSort(s, begin, end):
if end <= begin+1
return
let mid = Partition(s, begin, end)
QuickSort(s, begin, mid)
QuickSort(s, mid+1, end)
复杂度
设h i g h − l o w = k high - low = k hi g h − l o w = k ,则Partition函数的输入规模为T ( k ) T(k) T ( k ) ,其时间复杂度为O ( k ) O(k) O ( k ) 。
QuickSort函数的初始输入规模为T ( n ) T(n) T ( n ) ,调用Partition的输入规模为T ( n ) T(n) T ( n ) ,每次递归后输入规模为上一层的T ( n 2 ) T(\frac{n}{2}) T ( 2 n ) ,可得:
T ( n ) = 2 ⋅ T ( n 2 ) + O ( n ) \begin{matrix}
T(n) & = & 2 \cdot T(\frac{n}{2}) + O(n)
\end{matrix} T ( n ) = 2 ⋅ T ( 2 n ) + O ( n ) 类似MergeSort算法,该算法的时间复杂度为O ( n ⋅ l o g 2 n ) O(n \cdot log_2 n) O ( n ⋅ l o g 2 n ) ,因为所有操作都没有申请额外内存,是在原地完成,因此空间复杂度为O ( 1 ) O(1) O ( 1 ) 。
源码
QuickSort.h
QuickSort.cpp
测试
QuickSortTest.cpp