快速排序
什么是快速排序
- 快速排序和冒泡排序一样,也属于交换排序,通过元素之间的比较和交换位置来达到排序的目的。
- 快速排序在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边, 比它小的元素移动到数列的另一边,从而把数列拆解成两个部分。这种思路叫作分治法。
- 每一轮的比较和交换,需要把数组中的全部元素遍历一边,时间复杂度是O(n)。遍历次数,假如元素个数是n,平均情况下需要logn轮,因此快速排序算法总体的平均复杂度是O(nlogn)。
基准元素的选择
- 基准元素(pivot),在分治过程中,以基准元素为中心,把其他元素移动到它的左右两边。
- 一边情况下,以第1个元素为基准元素,但如果每轮第1个元素为最大(小)值,则时间复杂度退化成O(n2)。在这种情况下可以随机选择一个元素为基准元素,并且让基准元素和数列首元素交换位置。
- 快速排序的平均时间复杂度是O(nlogn),但最坏情况下的时间复杂度是O(n2)。
元素的交换
元素交换的方法:
-
双边循环法
- 【第一轮】选定基准元素pivot,并设置两个指针left和right,指向数列的最左和最右两个元素
- 【第一轮】 left和right指针与基准元素比较交换
- 【第二轮】重新切换left和right的指针
def quick_sort(start_index, end_index, array = []): # 递归结束条件:start_index大于等于end_index的时候 if start_index >= end_index: return # 得到基准元素的位置 pivot_index = partition_v1(start_index, end_index, array) # 根据基准元素,分成两部分递归排序 quick_sort(start_index, pivot_index - 1, array) quick_sort(pivot_index +1, end_index, array) def partiton_v1(start_index, end_index, array = []): # 取第一个位置的元素作为基准元素(也可以选择随机位置) pivot = array[start_index] left = start_index right = end_index while left != right: # 控制right指针进行比较并左移 while left < right and array[right] > pivot: right -= 1 # 控制left指针进行比较并右移 while left < right and array[left] <= pivot: left += 1 # 交换left指针和right指针指向的元素 if left < right: array[left], array[right] = array[right], array[left] # pivot 和指针重合点交换 array[start_index] = array[left] array[left] = pivot return left my_array = list([3, 4, 14, 1, 5, 6, 7, 8, 9, 1, -1, 0, 9, 11]) quicky_sort(0, len(my_array)-1, my_array) print(my_array) -
单边循环法
- 首先选定基准元素pivot,设置一个mark指针指向数列起始位置,这个mark指针代表小于基准元素的区域边界。
- 从基准元素的下一个位置开始遍历数组
- 如果遍历到的元素大于基准元素,就继续往后遍历。
- 如果遍历到的元素小于等于基准元素,第一步mark指针往右移动1位,第二部让当前遍历到的元素与当前mark指针元素交换位置。
- 最后把pivot元素交换到mark指针所在的位置,第一轮结束。
def quick_sort(start_index, end_index, array = []): # 递归结束条件:start_index大于等于end_index的时候 if start_index >= end_index: return # 得到基准元素的位置 pivot_index = partition_v1(start_index, end_index, array) # 根据基准元素,分成两部分递归排序 quick_sort(start_index, pivot_index - 1, array) quick_sort(pivot_index +1, end_index, array) def partition_v2(start_index, end_index, array = []): # 取第一个位置的元素作为基准元素(也可以随机选取) pivot = array[start_index] for i in range(start_index + 1, end_index + 1): if array[i] < pivot: mark += 1 array[mark], array[i] = array[i], array[mark] array[start_index] = array[mark] array[mark] = pivot return mark my_array = list([3, 4, 14, 1, 5, 6, 7, 8, 9, 1, -1, 0, 9, 11]) quicky_sort(0, len(my_array)-1, my_array) print(my_array)
非递归实现
绝大多数的递归逻辑,都可以用栈的方式来代替。
def quick_sort(start_index, end_index, array = []):
quick_sort_stack = []
# 整个数列的起止下表,以哈希表的形式入栈
root_param = {"startIndex": start_index, "endIndex": end_index}
quick_sort_stack.append(root_param)
# 循环结束条件:栈空时结束
while len(quick_sort_stack) > 0:
# 栈顶元素出栈,得到起止下表
param = quick_sort_stack.pop()
# 得到基准元素位置
pivot_index = partition(param.get("startIndex"),param.get("endIndex"),array)
# 根据基准元素分成两个部分,把每一部分的起止下标入栈
if param.get("startIndex") < pivot_index - 1:
left_param = {"startIndex": param.get("startIndex"), "endIndex": pivot_index - 1}
quick_sort_stack.append(left_param)
if pivot_index +1 <param.get("endIndex"):
right_param = {"startIndex": pivot_index + 1, "endIndex": param.get("endIndex")}
quick_sort_stack.append(right_param)
def partiton(start_index, end_index, array = []):
# 取第一个位置的元素作为基准元素(也可以选择随机位置)
pivot = array[start_index]
mark = start_index
for i in range(start_index + 1, end_index + 1):
if array[i]< pivot:
mark += 1
array[mark], array[i] = array[i], array[mark]
array[start_index] = array[mark]
array[mark] = pivot
return mark
my_array = list([3, 4, 14, 1, 5, 6, 7, 8, 9, 1, -1, 0, 9, 11])
quicky_sort(0, len(my_array)-1, my_array)
print(my_array)