十大经典排序算法--快速排序
1 快速排序的基本思想
快速排序(Quicksort)是对冒泡排序的一种改进。
它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
2 快速排序的三个步骤
1) 选择基准:在待排序列中,按照某种方式挑出一个元素,作为 “基准”(pivot);
2) 分割操作:以该基准在序列中的实际位置,把序列分成两个子序列。此时,在基准左边的元素都比该基准小,在基准右边的元素都比基准大;
3) 递归地对两个序列进行快速排序,直到序列为空或者只有一个元素;
3 选择基准元的方式
对于分治算法,当每次划分时,算法若都能分成两个等长的子序列时,那么分治算法效率会达到最大。也就是说,基准的选择是很重要的。选择基准的方式决定了两个分割后两个子序列的长度,进而对整个算法的效率产生决定性影响。
最理想的方法是,选择的基准恰好能把待排序序列分成两个等长的子序列。
方法1 固定基准元
如果输入序列是随机的,处理时间是可以接受的。如果数组已经有序时,此时的分割就是一个非常不好的分割。因为每次划分只能使待排序序列减一,此时为最坏情况,快速排序沦为冒泡排序,时间复杂度为Θ(n^2)。而且,输入的数据是有序或部分有序的情况是相当常见的。因此,使用第一个元素作为基准元是非常糟糕的,应该立即放弃这种想法。
def swap(arr,i,j): arr[i],arr[j]=arr[j],arr[i] def partition(arr,left,right): pivot = left index = pivot + 1 # 用来指向大于基准的数字 i = index # 用于进行循环 while i<=right: if arr[i]<arr[pivot]: swap(arr,i,index) index+=1 i+=1 swap(arr,pivot,index-1) return index-1 def quick_sort(arr,left,right): if left<right: divison = partition(arr,left,right) quick_sort(arr,left,divison-1) quick_sort(arr,divison+1,right) return arr arr=[3,8,2,4,9,0,1,5,7,6] quick_sort(arr,0,len(arr)-1) print(arr)
另一种更简单的快排如下:
#!/usr/bin/env python # coding:utf-8 def quick_sort(data): if len(data) <= 1: return data else: low_list = [] high_list = [] pivot = data[0] for item in data[1:]: if item <= pivot: low_list.append(item) else: high_list.append(item) return quick_sort(low_list)+[pivot]+quick_sort(high_list) if __name__ == "__main__": data = [1,2,5,7,9,8,0,3,4] print(quick_sort(data))
方法2 随机基准元
这是一种相对安全的策略。由于基准元的位置是随机的,那么产生的分割也不会总是会出现劣质的分割。在整个数组数字全相等时,仍然是最坏情况,时间复杂度是O(n^2)。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。
方法3 三数取中
引入的原因:虽然随机选取基准时,减少出现不好分割的几率,但是还是最坏情况下还是O(n^2),要缓解这种情况,就引入了三数取中选取基准。
分析:最佳的划分是将待排序的序列分成等长的子序列,最佳的状态我们可以使用序列的中间的值,也就是第N/2个数。可是,这很难算出来,并且会明显减慢快速排序的速度。这样的中值的估计可以通过随机选取三个元素并用它们的中值作为基准元而得到。事实上,随机性并没有多大的帮助,因此一般的做法是使用左端、右端和中心位置上的三个元素的中值作为基准元。
在固定基准基础上,使用三数取中
#!/usr/bin/env python # coding:utf-8 def quick_sort(data,left,right): if left < right: division = get_division(data,left,right) quick_sort(data,division+1,right) quick_sort(data,left,division-1) def get_division(data,left,right): #基准:三数取中 if data[left]<=data[right]: max = data[right] min = data[left] else: max = data[left] min = data[right] middle = (left+right)//2 if data[middle]>=max: target = max elif data[middle]<=min: target = min else: target = data[middle] #将基准放到最左侧 if target == data[right]: data[left],data[right]=data[right],data[left] elif target == data[middle]: data[left],data[middle]=data[middle],data[left] pivot = data[left] while left < right: while left < right and data[right] >= pivot: right -= 1 data[left] = data[right] while left < right and data[left] <= pivot: left += 1 data[right] = data[left] data[left] = pivot return left if __name__ == "__main__": data = [9,8,7,6,5,4,3,200,100] left = 0 right = len(data) - 1 quick_sort(data,left,right) print(data)
4 快速排序的优化
- 优化基准选择(三数取中)
- 优化小数组排序效率(插入排序)
- 优化交换次数
- 优化递归
- 优化最差情况,避免糟糕分区
- 元素聚合
- 一个细节是不是没提到。把pivot移到最右边,那么最好先让i从左往右扫描,再让j从右往左扫描。而如果把pivot放在最左边,那又最好先让j从右往左扫描,再让i从左往右扫描
参考链接:
https://zhuanlan.zhihu.com/p/40910407
https://zhuanlan.zhihu.com/p/57436476
https://blog.csdn.net/liuyi1207164339/article/details/50827608
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2017-02-21 使用appium和testng实现Android自动截图