常见的经典算法的实现

首先来一张图,这张图总结了常见的几种排序算法的时间复杂度和空间复杂度的对比。现对常见的几种算法进行实现,以备后需。图片来源:专知

1. 快速排序

思路

先寻找一个基准数,然后接下里的目的是要寻找一个位置,将这个基准数移动至该位置,要使得比该基准数小的所有数位于该基准的左侧,比该基准数大的所有数位于基准的右侧。该位置将整个要排序的数字划分为两段,然后分别对两段进行递归。

时间复杂度:平均复杂度O(nlog(n)), 最好O(nlog(n)), 最坏O(n^2), 不稳定

def QuickSort(self, l, left, right):

"""

:param left: 选中的基准位置

:param right: 列表的最后位置

"""

if len(l) == 0:

return

if left < right:

base = l[left] # 基准

i = left

j = right

# 1. 首先确定基准的位置

while i != j: # 当指针i和j不相遇的时候

while l[j] >= base and i < j:

j -= 1 # 首先从右向左遍历,寻找第一个比base小的数

while l[i] <= base and i < j:

i += 1 # 当j指针停止遍历时,i指针开始从左向右遍历,寻找第一个比base大的数

if i < j: # i和j 都停止遍历的时候,交换

tmp = l[i]

l[i] = l[j]

l[j] = tmp

l[left] = l[i] # i=j时,找到基准该呆的位置

l[i] = base

print(l)

# 2.基准将原始列表划分两部分,分别对两部分进行递归

self.QuickSort(l, left, i - 1)

self.QuickSort(l, i + 1, right)

对l = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8]使用快速排序的结果如下:

[3, 1, 2, 5, 4, 6, 9, 7, 10, 8]

[2, 1, 3, 5, 4, 6, 9, 7, 10, 8]

[1, 2, 3, 5, 4, 6, 9, 7, 10, 8]

[1, 2, 3, 4, 5, 6, 9, 7, 10, 8]

[1, 2, 3, 4, 5, 6, 8, 7, 9, 10]

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

2. 归并排序

思路

归并排序的过程可以由下图一张图来说明。图片来源:dreamcatcher-cx

归并排序主要分为两个阶段:分和治。在分阶段,就是采用二分的形式,依次将数组划分为两段,直到每段的长度为1;治阶段就是讲划分的子数组两两合并,再合并的时候进行排序。

时间复杂度:平均、最好、最坏的时间复杂度都是O(nlog(n))。稳定。

def MergeSort(self, l):

"""

思路:先将数组依次2等分, 然后再合并

"""

left = 0

right = len(l) - 1

tmp = ['#']*len(l) # 辅助数组, 用特殊字符来初始化

self.sort(l, left, right, tmp)

# 分阶段:递归

def sort(self, l, left, right, tmp):

if left < right:

mid = (left + right) // 2

self.sort(l, left, mid, tmp) # 划分左边

self.sort(l, mid + 1, right, tmp) # 划分右边

self.merge(l, left, mid, right, tmp) # 合并两个子数组

# 治阶段:合并

def merge(self, l, left, mid, right, tmp):

i = left

j = mid + 1

t = 0 # 这里的临时指针的作用就是在每次递归合并的时候,重新指向辅助数组的0位置,防止数组保留每次递归的所有值

while i <= mid and j <= right:

if l[i] <= l[j]:

tmp[t] = l[i]

t += 1

i += 1

else:

tmp[t] = l[j]

t += 1

j += 1

while i <= mid: # 将左子数组剩下的直接插入

tmp[t] = l[i]

t += 1

i += 1

while j <= right: # 将右子数组剩下的直接插入

tmp[t] = l[j]

t += 1

j += 1

# 将已经合并好的复制到原始的数组中

t = 0

while left <= right:

l[left] = tmp[t]

t += 1

left += 1

print(l)

对l = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8]使用归并排序的结果如下:

[1, 6, 2, 7, 9, 3, 4, 5, 10, 8]

[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]

[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]

[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]

[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]

[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]

[1, 2, 6, 7, 9, 3, 4, 5, 8, 10]

[1, 2, 6, 7, 9, 3, 4, 5, 8, 10]

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

3. 堆排序

思路

堆分最小堆和最大堆,最小堆的根节点是所有数中最小的,且每个父节点的值都小于其子节点的值;最大堆的根节点是所有数中最大的,且每个父节点的值都大于其子节点的值。堆排序的重点就是如何调整堆使其满足最小堆或者最大堆的要求。对于最大堆来说,对于当前节点,如果子节点的值比该节点大,那么就将子节点中的最大值和该节点交换,这样让最大的值慢慢向上浮,直到root节点。

堆的示意图如下。图片来源:dreamcatcher-cx

时间复杂度:平均、最好、最好时间复杂度都是O(nlog(n))

def HeapSort(self, l):

if not l:

return

for i in range(len(l) - 1, 0, -1):

print('第%s趟:%s' % (len(l) - i - 1, l[::-1]))

self.FitMaxHeap(l, 0, i)

l[i], l[0] = l[0], l[i]

# 采用非递归的方式来实现,并且是基于最大堆

def FitMaxHeap(self, arr, i, n):

j = 2 * i + 1 # 左孩子

curr = arr[i]

while j < n:

if j + 1 < n and arr[j + 1] < arr[j]:

j += 1

if curr <= arr[j]: # 没必要交换

break

else:

arr[i] = arr[j] # 交换

i = j

j = 2 * i + 1

arr[i] = curr

对l = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8]使用堆排序的结果如下:

[6, 1, 2, 7, 9, 3, 4, 5, 10, 8]

第1趟:[1, 10, 5, 4, 3, 9, 7, 2, 6, 8]

第2趟:[1, 2, 5, 4, 8, 9, 7, 3, 6, 10]

第3趟:[1, 2, 3, 10, 8, 9, 7, 4, 6, 5]

第4趟:[1, 2, 3, 4, 8, 9, 7, 5, 6, 10]

第5趟:[1, 2, 3, 4, 5, 9, 7, 10, 6, 8]

第6趟:[1, 2, 3, 4, 5, 6, 8, 10, 7, 9]

第7趟:[1, 2, 3, 4, 5, 6, 7, 10, 9, 8]

第8趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

4. 插入排序

思路

刚开始时以第1个元素作为已经排好序的,然后依次将后面的元素看做待插入的元素,依次移动到已排序的合适的位置进行插入;或者将待插入的元素与排好序的每个元素比较并交换,知道找到合适的位置插入。

下图展示了插入排序的思路。图片来源:带鱼兄

时间复杂度:平均复杂度O(n^2), 最好O(n), 最坏O(n^2),稳定

def InsertSort(self, l):

n = len(l)

print('第1趟:%s' % l)

for i in range(1, n):

tmp = l[i]

j = i

while tmp < l[j - 1] and j > 0: # [i, 0]逆序

l[j] = l[j - 1] # 依次后移

j -= 1

l[j] = tmp # 插入

print('第%s趟:%s' % (i + 1, l))

对l = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8]使用插入排序的结果如下:

[6, 1, 2, 7, 9, 3, 4, 5, 10, 8]

第1趟:[6, 1, 2, 7, 9, 3, 4, 5, 10, 8]

第2趟:[1, 6, 2, 7, 9, 3, 4, 5, 10, 8]

第3趟:[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]

第4趟:[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]

第5趟:[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]

第6趟:[1, 2, 3, 6, 7, 9, 4, 5, 10, 8]

第7趟:[1, 2, 3, 4, 6, 7, 9, 5, 10, 8]

第8趟:[1, 2, 3, 4, 5, 6, 7, 9, 10, 8]

第9趟:[1, 2, 3, 4, 5, 6, 7, 9, 10, 8]

第10趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

5. 希尔排序

思路

希尔排序的思路是通过设置一个增量gap,然后基于数组元素的下表将元素分成很多小的组,然后在每个组内使用最简单的插入排序进行排序;紧接着设置gap = gap / 2,继续分组和排序,直到最后的gap=1。

下图展示了一个gap的排序过程,图片来源:skywang12345

时间复杂度:平均O(nlog(n)^2), 最好O(nlog(n)), 最坏O(nlog(n)^2)

# 基于直接交换的插入排序

def ShellSort(self, l):

gap = len(l) // 2

while gap > 0: # 对于每个增量

for i in range(gap, len(l)): # 按照元素下表增量划分成组

j = i

# 直接交换法

while j - gap >= 0 and l[j] < l[j - gap]: # 每一组内进行插入排序

l[j - gap], l[j] = l[j], l[j - gap]

j -= gap

print(gap, l)

gap //= 2

# 基于移动的插入排序

def ShellSort2(self, l):

gap = len(l) // 2

while gap > 0:

for i in range(gap, len(l)):

j = i

tmp = l[i]

# 移动法

while j - gap >= 0 and tmp < l[j - gap]:

l[j] = l[j - gap]

j -= gap

l[j] = tmp

print(l)

gap //= 2

对l = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8]使用希尔排序的结果如下:

[6, 1, 2, 7, 9, 3, 4, 5, 10, 8]

gap=5:[3, 1, 2, 7, 8, 6, 4, 5, 10, 9]

gap=2:[2, 1, 3, 5, 4, 6, 8, 7, 10, 9]

gap=1:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

6. 冒泡排序

思路:冒泡排序的思路很简单,就是通过两两比较,然后将最小的或者最大的元素往后移动,每趟会确定一个次小的元素。

下图展示了冒泡的思路。图片来源:郭威gowill

时间复杂度:平均的复杂度O(n^2),最好O(nlog(n)), 最坏O(n^2),稳定

def BubbleSort(self, l):

if l is None:

return None

for i in range(len(l)):

print('第%s趟' % (i + 1))

for j in range(len(l) - (i + 1)):

if l[j] > l[j + 1]:

l[j], l[j + 1] = l[j + 1], l[j] # 交换

print(l)

对l = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8]使用冒泡排序的结果如下:

[6, 1, 2, 7, 9, 3, 4, 5, 10, 8]

第1趟:[1, 2, 6, 7, 3, 4, 5, 9, 8, 10]

第2趟:[1, 2, 6, 3, 4, 5, 7, 8, 9, 10]

第3趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

第4趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

第5趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

第6趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

第7趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

第8趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

第9趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

第10趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

7. 选择排序

思路:每一趟,选择数组中的最小的元素,然后依次交换位置,使其到达另一端。然后选择次小的。。。

时间复杂度: 平均时间复杂度O(n^2), 最好O(n), 最坏O(n^2),稳定

def SelectSort(self, l):

for i in range(len(l)):

index = i

for j in range(i, len(l)):

if l[j] < l[index]: # 选择最小的元素

index = j

if index != i: # 如果最小值的位置没有发生变化,则交换

l[i], l[index] = l[index], l[i]

print('第%s趟:%s' % (i+1, l))

对l = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8]使用选择排序的结果如下:

[6, 1, 2, 7, 9, 3, 4, 5, 10, 8]

第1趟:[1, 6, 2, 7, 9, 3, 4, 5, 10, 8]

第2趟:[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]

第3趟:[1, 2, 3, 7, 9, 6, 4, 5, 10, 8]

第4趟:[1, 2, 3, 4, 9, 6, 7, 5, 10, 8]

第5趟:[1, 2, 3, 4, 5, 6, 7, 9, 10, 8]

第6趟:[1, 2, 3, 4, 5, 6, 7, 9, 10, 8]

第7趟:[1, 2, 3, 4, 5, 6, 7, 9, 10, 8]

第8趟:[1, 2, 3, 4, 5, 6, 7, 8, 10, 9]

第9趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

第10趟:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

python五种常见的排序方法是_常见的经典的排序方法的实现(Python)相关推荐

  1. 独家 | 每个数据科学家应该知道的五种检测异常值的方法(附Python代码)

    作者:Will Badr 翻译:顾伟嵩校对:欧阳锦本文约1600字,建议阅读5分钟本文介绍了数据科学家必备的五种检测异常值的方法. 无论是通过识别错误还是主动预防,检测异常值对任何业务都是重要的.本文 ...

  2. Python语言学习:基于python五种方法实现使用某函数名【func_01】的字符串格式('func_01')来调用该函数【func_01】执行功能

    Python语言学习:基于python五种方法实现使用某函数名[func_01]的字符串格式('func_01')来调用该函数[func_01]执行功能 目录 问题提出 代码五种设计思路 T1方法:e ...

  3. python五种可视化工具及六道常见面试题

    2017-12-26  吊炸天的  [Python五种可视化工具] 在 Python 中,将数据可视化有多种选择,正是因为这种多样性,何时选用何种方案才变得极具挑战性.本文包含了一些较为流行的工具以及 ...

  4. python中的排序方法都有哪些_Python中的排序方法sort(),sorted(),argsort()等

    Python list内置sort()方法用来排序,也可以用python内置的全局sorted()方法来对可迭代的序列排序生成新的序列. 1)排序基础 简单的升序排序是非常容易的.只需要调用sorte ...

  5. Unity(游戏)中五种数据存储的方法

    Unity(游戏)中五种数据存储的方法 一.PlayerPrefs unity3d提供了一个用于本地持久化保存与读取的类-------PlayerPrefs.工作原理很简单,以键值对的形式将数据保存在 ...

  6. 五种常用异常值检测方法

    Toggle navigation 首页 产业趋势 专家观察 CISO洞察 决策研究 登录 APP下载 数据挖掘最前线:五种常用异常值检测方法 安全运营 机器之心 2019-07-05 通过鉴别故障来 ...

  7. 掌握常见的内部排序方法(插入排序,冒泡排序,选择排序,快速排序,堆排序,希尔排序,归并排序,基数排序等)...

    掌握常见的内部排序方法(插入排序,冒泡排序,选择排序,快速排序,堆排序,希尔排序,归并排序,基数排序等). 数组高级以及Arrays(掌握) 排序方法 空间复杂度 时间复杂度 稳定性 插 入 排 序 ...

  8. Server 2016DC查看五种AD角色的方法

    Server 2016DC查看五种AD角色的方法 https://jingyan.baidu.com/article/e8cdb32b87e4aa76042bad6c.html 听语音 原创 | 浏览 ...

  9. 五种外观缺陷检测方法,不分优劣,应用场景不同使用的技术不同

    工业外观缺陷检测方法详细介绍如下: 一.超声波探伤检测 超声波探伤检测是根据声波在缺陷处发生波形变化的原理来检测缺陷.声波在工件内的反射状况就会显示在屏幕上,根据反射波的时间及形状来判断工业制造件内部 ...

最新文章

  1. Java 两个引用类型相等_java-Spring配置:2个具有相同类引用的bean
  2. Powershell 音乐播放
  3. 两种过年烟花,你喜欢哪一种(HTML+CSS+JS)
  4. 连接mysql语言_杂谈各个语言连接数据库如何实现的-第一讲
  5. 新一代数据库技术在双11中的黑科技
  6. CentOS 7 搭建RAP2r Api文档管理系统
  7. 为什么Docker容器将占领世界
  8. vrep中的a dubins state space
  9. 为什么说强化学习是针对优化数据的监督学习?
  10. System.Drawing.Text.TextRenderingHint 的几种效果
  11. Quartus 实现D触发器及仿真
  12. 分享一些软件工具~截图工具
  13. 推荐支持 azw3 、epub 和 mobi 格式的阅读器:FBReader
  14. 大力发展职业技术教育,高等教育也要跟动车高铁一样降降速了
  15. 淘宝客是鸡肋还是熊掌
  16. 迁移学习论文阅读感想(初步)
  17. www.51zzl.com
  18. 如何解决mac拔掉耗电量太大的设备以重新启用usb设备
  19. js获取元素width和height
  20. mysql count里select_select count()和select count(1)的区别和执行方式讲解

热门文章

  1. java 2 实用教程(第五版)第六章课后编程题:设计一个动物声音模拟器,希望模拟器可以模拟许多的动物的叫声。
  2. 【有奖征集】 | 解锁程序YUAN的1024面
  3. 第三章 总线和存储器
  4. HDU 1878 欧拉回路(入门)
  5. 前端学习笔记-9.1怎样注册亚马逊aws免费1年云服务器?
  6. html交叉轴排列,CSS Flex 交叉轴水平方向
  7. AR | 增强现实简述
  8. 浅谈labviEW定时器
  9. 基于C#结合SQL设计学生成绩系统系统
  10. 搜索网站/论坛内容帖子