快速排序的时间复杂度为O(nlogn),空间复杂度为O(n)。根据@张小牛 的文章快速排序(Quick Sort)详解,证明最优的排序算法,其时间复杂度可为O(nlogn),对应的空间复杂度可为O(n)。快速排序可实现理论最优效率,这可能是快速排序比较重要的原因吧。

我们基于Python学习写一下快速排序吧。

先给定一个长度为10的列表data = [5, 4, 7, 8, 2, 7, 8, 5, 6, 3],如下:

初始列表

有了一个列表,看起来会直观多了吧,但是我们想放着不管。快速排序的主旨很简单:找一个标杆数,称为X,然后根据X把数组的数分堆,小于X的全放左边,大于X的全放右边,就可以啦。对于实际情况呢,我们还需要考虑等于X的情况,我将其与小于归为一起,即数组排列后,形成“小于等于X” + “大于X”两部分。

就是说,快速排序的主要步骤就是:找X + 跟X比大小排列?

你可能会疑惑,只是按“比X大或比X小”排列数组,怎么能得到完整的排序呢。一次排列几乎不可能排好,但我们可以将排了一次的数组上,切分为“小于等于X”和“大于X”两块,再对这两块分别再找标杆数X'和X'',接着再分别排序。最后组合再一起,就得到了排列了两次的数组,其顺序肯定更接近完美序列。那么我们继续这么“切分→找标杆数→排列”操作下去呢?在此例中,由于每一分堆总小于右侧的分堆,而大于左侧的分堆,同时每个分堆内部已排好序,因此整个序列排序完成。

以上这种操作叫做“递归”,可以对数组不断地切分并采用同样的排列模式进行排列,直到递归条件不再满足,则停止递归。在这里,我们选择切分后数组的长度大小,作为递归的条件,细节在后面详述。

我们不妨先试着试验一下,找X,然后将数组跟X比大小排列。我没有研究哪一个当标杆好,不如就选第一个数字吧。

选择第一个数字为“标杆数”

下面我们就要依据“标杆数”,也就是数字“5”(其序数为0),对其余部分进行分堆了。我们想分为“<=5”与“>5”的两部分,并使前者位于左侧,后者位于右侧,操作步骤如下:

1. 命名左侧序数为 i,初始 i = 1;命名右侧序数为 j,初始 j = len(data)-1(即最后一位)。

初始化i、j

2. 让j开始移动并进行判断:

若j所在的数字<=5,则让i开始向右移动,直到i所在的数字>5,接着交换data中i, j所对应的数字,即:

data[i], data[j] = data[j], data[i]

若j所在数字>5,则忽略,继续向左移动。

3. 当 j == i 时,意味着交换结束,列表除了首位的“标杆数”,其余部分分为<=5和>5两堆,那么我们还应该把“5”放到这两堆中间,让列表看上去更有序。即:

data[0], data[j] = date[j], data[0]

注意:如果j此时不在列表中间呢,比如由于数据特殊,j最终停在在首、尾处呢?

不能交换

可以交换

考虑到这一点,我们就可以意识到,要做的是把一开始找的标杆放到应有的位置上,即最后一个<=5的数的位置。因此,我们在交换前加一个判断:

if data[j] <= data[0]:

data[0], data[j] = data[j], data[0]

4. 结束操作,返回此时的data。

容易看到,由于我让j的移动占主动性,j先找到一个<=5的数后,i才能开始行动找>2的数。那么当j、i会,请问j最终会停在<=5还是>5的数字上呢?

答案是:对于这里给出的data,j总是会停在<=5的数字上,或者说对于len(data)>5的data,j与i总是相遇在最右的“<=标杆数”的位置上(仔细想一想~)。

这样做是为了便于data[j]与data[0]交换,所以让j先行;如果让i先行,那么相反的,i、j通常相遇在最左的“>标杆数”的位置上,这样不利于data[i]与data[0]交换。

对于len(data) == 1 or len(data) == 2的两种例外情况,留给读者思考。伪代码如下:

if len(data) == 2:

if data[j] <= data[0]:

data[0], data[j] = data[j], data[0]

return quicksort(data[left_part]) + quicksort(data[right_part])

if len(data) == 1:

return data

以上部分讲的是单次排序的(啰嗦)细节,整个快速排序是若干次单次排序的递归,下面讲解一下递归部分:

首先简化模型,我们把不直观的“数字比大小”转换为直观的“图形排序”,将data中的“标杆数5”及<=5的数替换为“☻”,将>5的数替换为“█”,则有:

接着,用上述的i,j排序规则操作一遍之后,得到:

是不是清晰许多?

进行递归,我们要做的就是把分大小排序的data拆分为两个data,分界线即为“标杆数”,然后分别对两个拆分data排序,直至抵达递归的回归条件(len(data) <= 1即终止)。

对示例列表进行快速排序的原理如下:

完整代码如下:

def quicksort(data): #快速排序

stone = data[0]

i = 1

j = len(data)-1

if len(data) > 1: #分为len(data) >2和len(data) == 2两种情况,可合并

while j > i:

if data[j] <= stone:

if data[i] > stone:

data[j], data[i] = data[i], data[j]

else:

i += 1

else:

j -= 1

if data[j] <= stone: #当len(data) == 2时只执行此部分

data[0], data[j] = data[j], data[0]

return quicksort(data[:j]) + quicksort(data[j:])

else: #回归条件,len(data) <= 1

return data

完结撒花~

以上 .

python快速排序的原理_理解快速排序算法相关推荐

  1. python tk mainloop原理_理解Tkinter mainloop()的逻辑以及为什么变量没有重新分配它们的原始值?...

    据我所知,window.mainloop()不断重复GUI代码,以便窗口及其小部件保持在屏幕上.因此,为什么一个变量(如canvastext)可以被更新并保持更新?难道window.mainloop( ...

  2. java随机数排序算法_理解快速排序算法

    快速排序在平均状况下,排序n个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n^2)次比较,但这种状况并不常见.事实上,快速排序通常明显比 其他Ο(n log n)算法更快,因为它的内部循环 ...

  3. 冒泡和快速排序的时间复杂度_八大排序算法性能分析及总结

    一.排序算法说明 排序的定义:对一个无序的序列进行排序的过程. 输入:n个数:a1,a2,a3,-,an. 输出:n个数的排列:a1,a2,a3,-,an,使得a1<=a2<=a3< ...

  4. python计算三角形面积_【Scratch算法编程】计算三角形面积

    [问题]已知一个三角形三条边的边长分别为a,b,c,利用海伦-秦九韶公式设计一个计算三角形面积的算法.(已知三角形三边边长分别为a b c,则三角形的面积为S= ),其中p= .这个公式被称为海伦-秦 ...

  5. l bfgs算法java代码_理解L-BFGS算法

    理解L-BFGS算法 Mar 30, 2015   #数值优化  #无约束最优化 L-BFGS(Limited-Memory BFGS)是BFGS算法在受限内存时的一种近似算法,而BFGS是数学优化中 ...

  6. python生成器yield原理_生成器yield关键字详解

    鉴于yield关键字的原理大家理解的都不是很深刻,今天我们主要就这一课题进行探讨. 生成器可以用什么方式得到? 方法一: 利用推导式的方式得到生成器# 列表推导式 list1 = [i for i i ...

  7. python 快速排序 详解_数据结构与算法:快速排序(原理讲解+python实现)

    快速排序 快速排序是一种基于分治法(Divide and Conquer)的排序算法 它之所以称为快速排序是因为它的平均时间复杂度为O(nlogn),最坏情况下是O(n2) 但是这样的情况不常见 一般 ...

  8. python遍历数组冒泡排序_经典排序算法(冒泡排序,选择排序,插入排序,快速排序,堆排序)python实现...

    最近在复习经典排序算法,自己用python也实现了一下,这里不会涉及到原理(因为网上方法已经很详细啦),就把函数贴上来,可以让大家自己试着运行下,再结合别处的原理也可以更好地理解它们的实现. 如果有错 ...

  9. python画爱心原理_如何理解python一行代码实现一个爱心字符画?

    前言 python中有个很酷的效果,一行代码实现一个爱心字符,虽说是一行代码,但是理解起来还是比较难的,括号太多,并且使用了python的一些快捷小技巧.比如三元表达式,列表生成式,字符串拼接以及一个 ...

  10. 冒泡和快速排序的时间复杂度_各种排序算法总结

    各种排序算法的稳定性,时间复杂度和空间复杂度总结: 我们比较时间复杂度函数的情况: 时间复杂度函数O(n)的增长情况: 所以对于n较大的排序记,一般的选择都是时间复杂度为O(nlog2n)的排序方法. ...

最新文章

  1. WPF中获取鼠标相对于屏幕的位置
  2. buffer java作用_Java NIO之Buffer的使用
  3. python学习框架图-Python学习—框架篇之初识Django
  4. 初学者浅谈我对领域驱动设计(DDD)的理解
  5. HBase shell 命令。
  6. Linux系统编程:循环创建N个子线程并顺序输出
  7. 每日编程-20170326
  8. Python+pandas统计每个学生学习慕课总时长
  9. iOS中Storyboard使用要点记录
  10. [答疑]请问商品领用审批的序列图画的对么
  11. python爬取同花顺_Python爬虫-同花顺行业历史数据及成分股
  12. c语言 美元符号,汇编语言 美元符号
  13. 亚当斯分区曝光法俗解之一
  14. C++“(已隐式声明)--它是已删除的函数 ” “尝试引用已删除的函数”知识点MARK
  15. Apache Calcite初探和csv简单例子
  16. 怒江java培训班_Graal VM:微服务时代的Java
  17. 加速度计和陀螺仪模型(imu元件)分析
  18. 从刘维尔方程到Velocity-Verlet算法
  19. Java 操作数据库插入失败原因
  20. OJ:Bad Cowtractors(最大生成树)

热门文章

  1. cocos2d-基本概念(5)-Effects 效果
  2. InputFilter实现EditText文本输入过滤器
  3. C++ vector 类学习笔记(转)
  4. Slave_SQL_Running: No mysql同步故障解决方法
  5. 微信小程序的开篇文章----小程序更新推荐
  6. 软件工程期末设计(校园教务系统)
  7. golang 时间戳和时间互转
  8. Layui动态修改列名
  9. Linux 64位的操作系统版本,查看cpu 以及linux和windows系统是32为还是64位?+查看操作系统版本...
  10. 压缩感知重构算法——SP算法