快速排序python实现总结
背景:数据结构与算法是IT相关的工程师一直以来的基础考察重点,很多经典书籍都是用c++或者java来实现,出于对python编码效率的喜爱,于是取search了一下python的快排实现,发现大家写的都比较个性,也所以我也总结下自己理解的python快排实现。
注:本随笔注重代码实现,如果是对快速排序无任何接触的还是先看一下相关的书籍
快速排序简介:快速排序是突破O(n^2)时间复杂度上界的排序算法,其平均情况下和最好情况是的时间复杂度都是O(nlogn),最差情况下的时间复杂度为O(n^2)(最差情况下退化为选择排序),空间复杂度为O(logn)
核心思想:
核心为 partition() 函数,该函数每调用一次,会产生两个作用:
例子:待排序数组为[3,5,1,8,2,4],调用一次该函数后数组变为[2,1,3,8,5,4]
直接作用:确定待排序数组上某个位置的值(我们称这个值为枢轴);在上例中表现为确定了待排序数组中索引为2(第3个元素)的值,元素'3'即为枢轴的值
副作用:将待排序数据分为了3个部分,即 [小于等于枢轴的待排序数组]+枢轴+[大于等于枢轴的待排序数组],副作用的贡献体现在减少了分治的次数
快速排序=对待排序数组采用分治+递归的方法调用partition()函数
partition()函数的时间复杂度为O(n),分治+递归调用的平均时间复杂度为O(logn),所以总体相乘为O(nlogn)
python代码实现
第一种实现,partition借助额外的list,所以partition函数的空间复杂度为O(n),因为涉及分治+递归调用,递归使用的隐含栈需要O(logn)的时间复杂度,所以整体空间复杂度为O(nlogn),借助额外的数据结构一般会起到两个效果:1、降低时间复杂度 或者 2、提高代码可读性(易于理解),这里并没有降低时间复杂度
def quick_sort1(lst):
"""快速排序"""
def partition(lst, left, right):
#借助两个临时列表存放小于枢轴的元素和大于枢轴的元素
l_list, r_list = [], []
#选取待排序列表的最左元素作为枢轴
pivot_value = lst[left]
for i in lst[left+1:right+1]:
if i<=pivot_value:
l_list.append(i)
else:
r_list.append(i)
#因为是原地排序,所以对原待排序数组的相应元素进行替换
lst[left:right+1] = l_list+[pivot_value]+r_list
return left+len(l_list) def q_sort(lst, left, right):
"""辅助函数,便于递归调用"""
if left>=right:
return
pivot_key = partition(lst, left, right)
q_sort(lst, left, pivot_key-1)
q_sort(lst, pivot_key+1, right) if not lst or len(lst)==0:
return lst q_sort(lst, 0, len(lst)-1) return lst
上述实现采用了额外的list,虽然增加了可读性,但是提高了空间复杂度,所以,可以对其优化,将partition函数的空间复杂度降为O(1)
第二种实现,不借助额外列表
def quick_sort2(lst):
"""快速排序"""
def partition(lst, left, right):
#默认选择列表最左元素作为枢轴
pivot_value = lst[left]
while left<right:
while left<right and lst[right]>=pivot_value:
right-=1
#当右指针对应元素小于枢轴的值,将左右指针对应元素交换,使小于枢轴的值位于枢轴的左侧
lst[left], lst[right] = lst[right], lst[left]
while left<right and lst[left]<=pivot_value:
left+=1
#当左指针对应元素大于枢轴的值,将左右指针对应元素交换,使大于枢轴的值位于枢轴的右侧
lst[left], lst[right] = lst[right], lst[left]
return left def q_sort(lst, left, right):
if left>=right:
return
pivot_key = partition(lst, left, right)
q_sort(lst, left, pivot_key-1)
q_sort(lst, pivot_key+1, right) if not lst or len(lst)==0:
return lst q_sort(lst, 0, len(lst)-1) return lst
这里通过元素交换的方式达到了与方法1同样的效果,所以在很多资料上,快速排序和冒泡排序都被分类为'交换排序',但有一点要注意,快速排序最差的情况下,会退化为选择排序而非冒泡排序
针对第二种情况,我们还可以继续优化,省去不必要的交换,将"交换"优化为“替换”
第三种实现
def quick_sort3(lst):
"""快速排序"""
def partition(lst, left, right):
#默认选择列表最左元素作为枢轴,同时也记录了left最初对应的元素值
pivot_value = lst[left]
while left<right:
while left<right and lst[right]>=pivot_value:
right-=1
#将left对应的元素替换为right(小于枢轴)对应的元素
lst[left] = lst[right]
while left<right and lst[left]<=pivot_value:
left+=1
#将right对应的元素替换为left(大于枢轴)对应的元素
lst[right] = lst[left]
#当left和right相等时,使用最初记录的left对应的元素值替换当前指针的元素
lst[left] = pivot_value
#返回枢轴对应的索引
return left def q_sort(lst, left, right):
if left>=right:
return
pivot_key = partition(lst, left, right)
q_sort(lst, left, pivot_key-1)
q_sort(lst, pivot_key+1, right) if not lst or len(lst)==0:
return lst q_sort(lst, 0, len(lst)-1) return lst
第三种方案和前两种一样,都是将列表的最左元素作为枢轴,这也是导致快速排序最差情况时间复杂度为O(n^2)的原因,比如每次列表的最左元素都为最大值或者最小值,那每次对partition函数的调用只起到了直接作用(确定了列表的最左端的最小值或者最右端的最大值),而没有起到副作用(副作用的目的是减小分治次数)
所以我们可以对枢轴的选取进行优化,优化的目的是使枢轴的选取避开最大值或最小值,尽量靠近中位数,优化的思路有两种
1、随机选取
2、选取列表中left, right, (left+right)//2,三个索引位置对应元素居中的元素
由于随机数的生成在编程语言API中的实现也要耗费一定的时间复杂度,所以我们选择2
第四种实现如下
def quick_sort4(lst):
"""快速排序"""
def partition(lst, left, right):
#计算中间索引
mid = (left+right)//2
#将三个元素中大小居中的元素交换至列表的最左侧
if lst[left]>lst[mid]:
lst[left], lst[mid] = lst[mid], lst[left]
if lst[mid]>lst[right]:
lst[mid], lst[right] = lst[right], lst[mid]
if lst[left]<lst[mid]:
lst[left], lst[mid] = lst[mid],lst[left] pivot_value = lst[left]
while left<right:
while left<right and lst[right]>=pivot_value:
right-=1
lst[left] = lst[right]
while left<right and lst[left]<=pivot_value:
left+=1
lst[right] = lst[left]
lst[left] = pivot_value
return left def q_sort(lst, left, right):
if left>=right:
return
pivot_key = partition(lst, left, right)
q_sort(lst, left, pivot_key-1)
q_sort(lst, pivot_key+1, right) if not lst or len(lst)==0:
return lst q_sort(lst, 0, len(lst)-1) return lst
经过2~4的优化,我们已经
1)把空间复杂度由O(nlogn)降至O(n),yi
2)并尽量优化了最差情况下的时间复杂度,使其比O(n^2)要好一些
但需要提醒一下,其最佳情况下的时间复杂度依旧使O(nlogn),而一些简单排序算法,如插入排序和优化后的冒泡排序的最优时间复杂度都可以达到O(n)
快排在面对大量数据排序时表现良好,
所以可以进行优化,当待排序数据的元素数量小于某个常数值时采用插入排序,否则使用快速排序
第五种实现
def quick_sort5(lst):
"""快速排序"""
def partition(lst, left, right):
#计算中间索引
mid = (left+right)//2
#将三个元素中大小居中的元素交换至列表的最左侧
if lst[left]>lst[mid]:
lst[left], lst[mid] = lst[mid], lst[left]
if lst[mid]>lst[right]:
lst[mid], lst[right] = lst[right], lst[mid]
if lst[left]<lst[mid]:
lst[left], lst[mid] = lst[mid],lst[left] pivot_value = lst[left]
while left<right:
while left<right and lst[right]>=pivot_value:
right-=1
lst[left] = lst[right]
while left<right and lst[left]<=pivot_value:
left+=1
lst[right] = lst[left]
lst[left] = pivot_value
return left def q_sort(lst, left, right):
if left>=right:
return
pivot_key = partition(lst, left, right)
q_sort(lst, left, pivot_key-1)
q_sort(lst, pivot_key+1, right) if not lst or len(lst)==0:
return lst
#取某个常数,待排序元素数量大于该常数时使用快排,否则使用插入排序
if len(lst)>50:
q_sort(lst, 0, len(lst)-1)
else:
#插入排序在此不实现了,大家自行解决
insert_sort(lst) return lst
经过上述优化,我们做到了
1)空间复杂度由O(nlogn)优化至O(logn)
2) 将最差情况下的时间复杂度O(n^2)尽可能提升
3)将时间复杂度的下界提升至O(n),当然,这已经不是单纯的快排了- -!
刚开始写博客,有不对的地方还望指教~~~
快速排序python实现总结的更多相关文章
- 快速排序--Python实现
快速排序算法:1.选择一个基准数2.小于基准数的放左边,大于基准数的放右边3.利用递归的方法针对左边的数据进行快速排序,再对右边的数据进行快速排序4.递归停止的条件:数组为空或者只有一个元素 时间复杂 ...
- 快速排序python实现
#--×--coding:utf-8-*- def main(): nlist = [] while 1: tmp = raw_input("Please input your elemen ...
- 快速排序(python版)
#!coding:utf8 def quicksort(list_num, left, right): if left > right: return low = left high = rig ...
- 快速排序-python
- python数据结构与算法
最近忙着准备各种笔试的东西,主要看什么数据结构啊,算法啦,balahbalah啊,以前一直就没看过这些,就挑了本简单的<啊哈算法>入门,不过里面的数据结构和算法都是用C语言写的,而自己对p ...
- 常见排序算法-Python实现
常见排序算法-Python实现 python 排序 算法 1.二分法 python 32行 right = length- : ] ): test_list = [,,,,,, ...
- python实现简单排序算法
算法 递归两个特点: 调用自身 有穷调用 计算规模越来越小,直至最后结束 用装饰器修饰一个递归函数时会出现问题,这个问题产生的原因是递归的函数也不停的使用装饰器.解决方法是,只让装饰器调用一次即可,那 ...
- <算法图解>读书笔记:第4章 快速排序
第4章 快速排序 4.1 分而治之 "分而治之"( Divide and conquer)方法(又称"分治术") ,是有效算法设计中普遍采用的一种技术. 所谓& ...
- Python和Java的语法对比,语法简洁上python的确完美胜出
Python是一种广泛使用的解释型.高级编程.通用型编程语言,由吉多·范罗苏姆创造,第一版发布于1991年.可以视之为一种改良(加入一些其他编程语言的优点,如面向对象)的LISP.Python的设计哲 ...
随机推荐
- php启动后netstat看不到9000端口的问题
https://www.cnblogs.com/jonsea/p/5522018.html php-fpm配置文件详解 其实就是PHP配置文件改一个参数 listen = 127.0.0.1: ...
- 104)PHP,目录树状输出
使用特定数量的缩进达到树状目的! 核心问题,计算需要缩进的数量! 缩进级别,与递归调用深度保持一致.每当执行一级递归操作,所找到的文件的缩进级别+; 语法实现: 增加一个参数,表示当前函数调用的深度级 ...
- 吴裕雄--天生自然 JAVASCRIPT开发学习:JavaScript 对象 实例
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- 系统学习javaweb1----HTML语言1
自我感受:HTML语言没想到也有这么大的学问,竟然能通过超链接标签直接访问百度,这可让我大吃一惊,我也得反思一下自己,上学期的java纯是混过来的,没有系统的学习过,感觉能通过期末考试都是侥幸,接下来 ...
- Python与数据库
链接汇总 https://www.cnblogs.com/stacklike/category/1134822.html Python与数据库[1] -> 数据库接口/DB-API[0] -&g ...
- 从源码看commit和commitAllowingStateLoss方法区别
Fragment介绍 在很久以前,也就是我刚开始写Android时(大约在2012年的冬天--),那时候如果要实现像下面微信一样的Tab切换页面,需要继承TabActivity,然后使用TabHost ...
- git 忽略规则
# 以'#'开始的行,被视为注释. # 忽略掉所有文件名是 foo.txt的文件. foo.txt # 忽略所有生成的 html文件, *.html # foo.html是手工维护的,所以例外. !f ...
- 吴裕雄--天生自然python学习笔记:Python3 模块
Python3 模块 在前面的几个章节中我们脚本上是用 python 解释器来编程,如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了. 为此 Python 提供了一个办 ...
- (转)python中join()方法
原文:http://blog.csdn.net/weixin_40475396/article/details/78227747 函数:string.join() Python中有join()和os. ...
- 转:B树和B+树的插入、删除图文详解
如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. B树 1. B树的定义 B树也称B-树,它是一颗多路平衡查找树.我们描述一颗B树时需要指定它的阶数,阶数表示了 ...