快速排序简介
快速排序(英语:Quicksort),又称划分交换排序(partition-exchange sort),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
步骤为:
从数列中挑出一个元素,称为”基准”(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
代码实现
def quick_sort1(arr):
if len(arr) < 2:
return arr # 基线条件:数列为空或者为1
else:
pivot = arr[0] # 递归条件
less = [i for i in arr[1:] if i <= pivot]
greater = [i for i in arr[1:] if i > pivot]
return quick_sort1(less) + [pivot] + quick_sort1(greater)
算法优化
快速排序的速度取决于选取的基准值,选取不同基准值时时间复杂度变化如下:
平均(取随机值为基准):O(nlogn)
最佳(取中间值为基准):O(nlogn)
最糟(取第一个值为基准):O(n^2)
针对这一点,可以选取中间值作为基准,代码如下:
def quick_sort2(alist, start, end):
"""快速排序"""
if start >= end: # 递归结束基准
return
mid = alist[start] # 设定基准元素
low = start
high = end
while low < high:
while low < high and alist[high] >= mid: # 如果该元素大于基准
high -= 1 # 不对该元素做处理,移动下标处理下一个元素
# 如果元素小于基准
alist[low] = alist[high]
while low < high and alist[low] < mid:
low += 1
# 将low指向的元素放到high的位置上
alist[high] = alist[low]
# 循环结束,low与high重合,指向基准元素的位置
alist[low] = mid
# 对基准值右边的元素进行快速排序
quick_sort2(alist, start, low - 1)
# 对基准值左边的元素进行快速排序
quick_sort2(alist, low + 1, end)
两者的速度比较如下:
li = [41, 45, 44, 2, 78, 62, 25, 34, 65]
q1 = timeit.Timer("quick_sort1(li)", "from __main__ import quick_sort1, li")
q2 = timeit.Timer("quick_sort2(li, 0, len(li) - 1)", "from __main__ import quick_sort2, li")
print(q1.timeit(number=1))
print(q2.timeit(number=1))
1.6820492118368674e-05
9.846141727825561e-06
可见明显快了!
时间复杂度
最优时间复杂度:O(nlogn)
最坏时间复杂度:O(n2)
稳定性:不稳定