1、快速排序思路(拓:O(n) 时间复杂度内求无序数组中的第 K 大元素)
根据分治、分区、递归的处理思想,我们可以用递归排序下标从 low到 m-1 之间的数据 和 下标从 m+1 到 high之间的数据 ,直到区间缩小为 1,就说明所有的数据都有序了。
核心思想就是写一个分区函数partition(),返回分区点的下标位置,使分区点左侧的数小于pivot,右侧的值大于pivot
递推公式和伪代码如下
递推公式:
quick_sort(p…r) = quick_sort(p…q-1) + quick_sort(q+1, r)
终止条件:
p >= r
伪代码:
// 快速排序,A 是数组,n 表示数组的大小
quick_sort(A, n) {
quick_sort_c(A, 0, n-1)
}
// 快速排序递归函数,p,r 为下标
quick_sort_c(A, p, r) {
if p >= r then return
q = partition(A, p, r) // 获取分区点
quick_sort_c(A, p, q-1)
quick_sort_c(A, q+1, r)
}
partition(A, p, r) {
pivot := A[r]
i := p
for j := p to r-1 do {
if A[j] < pivot {
swap A[i] with A[j]
i := i+1
}
}
swap A[i] with A[r]
return i
2、关键:(原地)分区函数
快慢指针“单边循环法”,j指针代表小于基准元素的区域边界
用“交换”的思想,只需要将 A[i] 与 A[j] 交换,就可以在 O(1) 时间复杂度内将 A[j] 放到下标为 i 的位置。
'''
原地分区函数
对 A[p…r] 分区,函数返回 pivot(分区点) 的下标
遍历数据,将小于 pivot 的放到左边,将大于 pivot 的放到右边,将 pivot 放到中间
'''
def _partition(a, low, high):
pivot, j = a[low], low # pivot是首位元素,j 是慢指针
for i in range(low+1, high+1): # i 是快指针,扫描从low+1位置到high位置
if a[i] <= pivot: # 快指针i 找到“较小的数”,将“较小的数”往前放
j += 1
a[j], a[i] = a[i], a[j]
a[low], a[j] = a[j], a[low] # 将pivot值放在中间,即index为j的位置
return j
3、代码详解
import random
def quick_sort(a):
_quick_sort_between(a, 0, len(a) - 1)
def _quick_sort_between(a, low, high):
if low < high:
# get a random position as the pivot
k = random.randint(low, high) # k是(low,high)之间的随机整数
a[low], a[k] = a[k], a[low]
m = _partition(a, low, high) # a[m] is in final position
_quick_sort_between(a, low, m - 1)
_quick_sort_between(a, m + 1, high)
'''
原地分区函数
对 A[p…r] 分区,函数返回 pivot(分区点) 的下标
遍历数据,将小于 pivot 的放到左边,将大于 pivot 的放到右边,将 pivot 放到中间
'''
def _partition(a, low, high):
pivot, j = a[low], low
for i in range(low + 1, high + 1):
if a[i] <= pivot:
j += 1
a[j], a[i] = a[i], a[j] # swap
a[low], a[j] = a[j], a[low]
return j
if __name__ == "__main__":
a1 = [3, 5, 6, 7, 8]
a2 = [2, 2, 2, 2]
a3 = [4, 3, 2, 1]
a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9]
quick_sort(a1)
print(a1)
quick_sort(a2)
print(a2)
quick_sort(a3)
print(a3)
quick_sort(a4)
print(a4)