快速排序同冒泡排序一样,也是属于交换排序,通过比较并交换元素位置来达到排序的最终效果
但不同的是,冒泡排序只是把其中选择出来的1个元素交换到数列的一侧
快速排序是在每一轮的交换比较过程中,每次选择一个基准元素,把数列当中比基准元素大的元素移动到它的一侧,比基准元素小的元素移动到它的另外一侧,循环往复最终排好顺序
快速排序总体的平均时间复杂度是在O(nlogn)
但是在最坏的情况下依然有O(n^2)的可能
快速排序的一个很重要的核心问题就是基数的选择
那么基数的选择一共有两种方式:
1、可以将数列的首个元素作为基数;
2、也可以随机的选取一个数列当中的元素作为基数
基数选择好之后就看具体实现的方法了,实现的方法有两种:
1、双边循环
2、单边循环
举一个简单的例子:
用随机生成的列表:[17, 14, 30, 8, 20, 2, 8, 23]
在这个列表当中用下标为0的元素作为基数pivot,也就是17,在最左右两侧各设置一个指针,分别为left,right
下面来实现第一轮,第一次遍历
pivot
[17, 14, 30, 8, 20, 2, 8, 23]
left right
一开始,动right指针,让right指针所指的元素和pivot进行比较,如果大于等于pivot,那right指针就向左移动一位;如果小于pivot,则right指针停止,切换到left指针
在当前的列表中23>17,则向左移动,比较之后发现8<17,则right指针指向8,停止
pivot
[17, 14, 30, 8, 20, 2, 8, 23]
left right
此时,轮到left指针,让left指针指向的元素和pivot进行比较,如果小于等于pivot,则left指针向右移动;如果大于pivot,则指针停止,一开始的left和pivot是一个元素,所以值相等,left向右移动一位,指向14,发现14小于17
pivot
[17, 14, 30, 8, 20, 2, 8, 23]
left right
那么left指针向右再移动一位,到30,发现17<30,则left指针停止
pivot
[17, 14, 30, 8, 20, 2, 8, 23]
left right
停止之后,交换left和right指针所指向的对应的值,
pivot
[17, 14, 8, 8, 20, 2, 30, 23]
left right
发生数组元素的交换之后,开始第一轮,第二次的遍历
依旧从right指针开始,由于right指向的值是30>17,所以right指针向左移动
pivot
[17, 14, 8, 8, 20, 2, 30, 23]
left right
当right 指向到2之后,2<17,right指针停止,此时轮到left指针,由于8<17,所以右移一位
pivot
[17, 14, 8, 8, 20, 2, 30, 23]
left right
但此时,当前left指向的值8,依然小于17,所以left指针继续右移
pivot
[17, 14, 8, 8, 20, 2, 30, 23]
left right
此时left指向的20是大于pivot的,left指针停止,停止之后,left和right指针各自指向的值,发生交换
pivot
[17, 14, 8, 8, 2, 20, 30, 23]
left right
发生交换之后开始第一轮,第三次遍历
重新回到right指针,20<17,right指针向左移动。此时,right和left重合
pivot
[17, 14, 8, 8, 2, 20, 30, 23]
left
right
重合之后,将pivot的元素和left、right指针重合的元素进行交换
pivot
[2, 14, 8, 8, 17, 20, 30, 23]
left
right
交换元素之后,第一轮遍历完成,开始下一轮的遍历,之后同理
def quick_sort_use(startIndex, endIndex, randomList):
'''
快速排序双向循环
:param startIndex: 开始位置下标
:param endIndex: 结束位置下标
:param randomList: 创建的用于排序的随机数组
:return: 左右指针
'''
# 选择列表当中第一个元素作为基数
choose_pivot = randomList[startIndex]
# 确定left指针
left_pointer = startIndex
# 确定right指针
right_pointer = endIndex
# 准备遍历
while left_pointer != right_pointer:
isRightExchange = True
# right指针的移动
if left_pointer < right_pointer and choose_pivot <= randomList[right_pointer]:
right_pointer -= 1
isRightExchange = False
isLeftExchange = True
# left指针移动
if left_pointer < right_pointer and choose_pivot >= randomList[left_pointer]:
left_pointer += 1
isLeftExchange = False
if isLeftExchange and isRightExchange and left_pointer < right_pointer:
randomList[left_pointer], randomList[right_pointer] = randomList[right_pointer], randomList[left_pointer]
# 左右指针重合之后,与pivot进行交换
randomList[startIndex] ,randomList[left_pointer] = randomList[left_pointer], choose_pivot
return left_pointer , right_pointer # 返回left指针, right指针
num_list = [27, 30, 24, 39, 25, 46, 26, 29, 20]
# 递归实现快排
def use_quickSort(startIndex, endIndex, randomList):
if startIndex >= endIndex:return
pivot = quick_sort_use(startIndex, endIndex, randomList)
use_quickSort(startIndex, pivot[0] - 1, randomList) # 左边
use_quickSort(pivot[1] + 1 ,endIndex, randomList) # 右边
# 调用快排
use_quickSort(0, len(num_list) - 1, num_list)
print('双边循环结果:\n%s'%num_list)
单边循环,顾名思义从数列的一侧进行遍历
参考数列如下:
[15, 17, 10, 3, 6, 9, 10, 4]
首先,选中一个元素作为基数:
pivot [15, 17, 10, 3, 6, 9, 10, 4]
再设置一个pointer指针,指向数列起始位置
pivot [15, 17, 10, 3, 6, 9, 10, 4]
p
准备好之后,开始遍历
从基数的下一个元素开始遍历,如果遍历到的元素大于基数,则接着向后遍历
如果遍历到的元素小于基数,则pointer指针向右移动一位,然后将遍历的元素,和移动pointer指针之后所指的元素进行交换
第一轮开始:
从15的下一个元素开始遍历,17大于15,因此接着向后遍历
pivot [15, 17, 10, 3, 6, 9, 10, 4]
p
遍历到10的时候,发现,10<15,此时pointer指针向右移动一位,指向17
pivot [15, 17, 10, 3, 6, 9, 10, 4]
p
交换二者的位置
pivot [15, 10, 17, 3, 6, 9, 10, 4]
p
完成交换之后接着向后遍历
发现3<15,此时pointer指针向右移动一位
pivot [15, 10, 17, 3, 6, 9, 10, 4]
p
交换二者位置
pivot [15, 10, 3, 17, 6, 9, 10, 4]
p
接着向后遍历,6<15,pointer指针随即向右移动一位
pivot [15, 10, 3, 17, 6, 9, 10, 4]
p
开始交换
pivot [15, 10, 3, 6, 17, 9, 10, 4]
p
交换之后接着遍历,9<17,pointer右移一位
pivot [15, 10, 3, 6, 17, 9, 10, 4]
p
开始交换
pivot [15, 10, 3, 6, 9, 17, 10, 4]
p
交换之后,继续遍历,后面同理,全部遍历完成之后的结果如下:
pivot [15, 10, 3, 6, 9, 10, 4, 17]
p
此时第一轮全部遍历完成,将pointer所指的元素和pivot互换
pivot [4, 10, 3, 6, 9, 10, 15, 17]
p
第一轮完成,分治法后,开始递归各自剩下的区域
def unidirection_quick_sort(startIndex, endIndex, num_list):
'''
处理快排的单边循环
:param startIndex:开始位置下标
:param endIndex:结束位置下标
:param num_list:待排序的数组
:return:pointer指针
'''
pivot = num_list[startIndex]
pointer = startIndex
for i in range(startIndex + 1, endIndex + 1): # 循环遍历整个数列
if num_list[i] < pivot:
pointer += 1
num_list[i], num_list[pointer] = num_list[pointer], num_list[i]
# 一趟遍历完成之后,交换pointer指向的值和pivot
num_list[startIndex], num_list[pointer] = num_list[pointer], num_list[startIndex]
return pointer
同样递归实现方法的调用
quick_num = [15, 17, 10, 3, 6, 9, 10, 4]
use_unidirection_quick_sort(0, len(quick_num) - 1, quick_num)
print('单边循环结果:\n%s'%quick_num)