一篇夯实一个知识点系列--python实现十大排序算法
写在前面
排序是查找是算法中最重要的两个概念,我们大多数情况下都在进行查找和排序。科学家们穷尽努力,想使得排序和查找能够更加快速。本篇文章用Python实现十大排序算法。
干货儿
排序算法从不同维度可以分为好多类别,从其排序思想(排序思想一般决定了其时间复杂度的量级)来看,主要可以分为四类:
- 双层循环比较排序:平方级排序
 - 分治策略比较排序:对数级排序
 - 另辟蹊径的非比较方式排序:线性级排序
 - 笑死人不偿命的其它排序:有着天马行空的时间复杂度,难以描述。
 
平方级排序
冒泡排序
- 从数组的第一个元素开始,比较当前元素和下一个元素,如果当前元素大于下一个元素,交换两元素位置。
 - 接着从第二个元素开始,重复第一步,直到当前元素为最后一个元素。此时最后一个元素为最大元素。未排序数组为除最后一个元素之外的其它元素。
 - 对未排序数组不断重复以上步骤,直到未排序数组为空。
 
def bubble_sort(arr):
length = len(arr)
for i in range(length):
for j in range(length-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
选择排序
- 选取数组中的最小元素,和数组中的第一个元素交换位置
 - 选取数组中除第一个元素外剩余元素的最小元素,和数组中的第二个元素交换位置。
 - 不断重复以上步骤,直到当前选取的元素为数组中最后一个元素。
 
def select_sort(arr):
length = len(arr)
for i in range(length):
min_ix = i
for j in range(i, length):
if arr[j] < arr[min_ix]:
min_ix = j
arr[min_ix], arr[i] = arr[i], arr[min_ix]
return arr
插入排序
- 从数组的第一个元素开始,不断比较当前元素和前一个元素。如果当前元素比前一个元素小,那么就将当前元素插入到前一个元素的前面(即两者交换位置)
 - 从第二个元素开始,不断重复以上步骤,直到所有元素全部经历上述步骤。
 
def insert_sort(arr):
length = len(arr)
for i in range(length):
for j in range(i, 0, -1):
if arr[j] < arr[j-1]:
arr[j], arr[j-1] = arr[j-1], arr[j]
return arr
对数级排序
希尔排序
- 选择一个增量值k,分别将数组中索引以k为间隔的元素放在同一个数组中。
 - 将增量值缩小为原增量值的1/2,然后重复步骤1。
 - 直到增量值为1,使用插入排序对已经部分有序的数组进行排序。
 
def shell_sort(arr):
n = len(arr)
gap = int(n/2)
while gap > 0:
for i in range(gap,n):
temp = arr[i]
j = i
while j >= gap and arr[j-gap] >temp:
arr[j] = arr[j-gap]
j -= gap
arr[j] = temp
gap = int(gap/2)
return arr
归并排序
- 以数组中间元素为界,将数组分为等长的两个数组(可能不等长,和数组长度的奇偶性有关)。
 - 对所有数组执行步骤1
 - 不断重复以上步骤,直到将数组分割为多个包含单个元素的数组。
 - 将以上数组两两合并,并排序,此时为多个包含有序的两个元素的数组(可能包含单个元素,跟数组长度的奇偶性有关)。
 - 重复步骤4,直到将所有数组合并为一个数组
 
def merge(left, right):
i = j = 0
res = []
while i < len(left) and j < len(right):
if left[i] < right[j]:
res.append(left[i])
i += 1
else:
res.append(right[j])
j += 1
if i == len(left):
res.extend(right[j:])
else:
res.extend(left[i:])
return res def merge_sort(arr):
if len(arr) <= 1:
return arr
length = len(arr)
i = int(length / 2)
left = merge_sort(arr[:i])
right = merge_sort(arr[i:])
return merge(left, right)
快速排序
- 挑选一个元素为基准
 - 比基准大的元素作为一个数组,比基准小或者等于基准的元素作为一个数组。
 - 对新分割的数组,不断重复以上步骤,直到分割后的数组只含有1个或者0个元素
 - 递归地合并以上数组为有序数组,合并方式为:[小于等于基准的元素]+[基准]+[大于基准的元素]
 
def fast_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr.pop()
left = [i for i in arr if i <= pivot]
right = [i for i in arr if i > pivot]
return fast_sort(left) + [pivot] + fast_sort(right)
以上算法需要额外的空间,如果我们将小于等于基准的元素不断置于基准元素之前,大于基准的元素置于基准元素之后,那么就可以实现不需要额外空间的就地排序。
def fast_sort_on_extra_spacing(arr):
l = 0
h = len(arr)-1 def partition(arr, l, h):
pivot = arr[h]
for i in range(l, h):
if arr[i] <= pivot:
arr[l], arr[i] = arr[i], arr[l]
l += 1
arr[h], arr[l] = arr[l], arr[h]
return l def fast_sort(arr, l, h):
if l < h:
pivot = partition(arr, l, h)
fast_sort(arr, l, pivot-1)
fast_sort(arr, pivot+1, h)
return arr
return fast_sort(arr, l, h)
堆排序
- 先对待排序数组构造大根堆
 - 将大根堆第一个元素和最后一个元素交换位置。此时最后一个元素为最大元素,待排序数组为除最后一个元素之外的所有元素。
 - 对待排序数组不断重复以上步骤,直到待排序数组中只有一个元素。
 
def heapify(arr, n, i):
# build a max root heap
max_ix = i
left_i = 2 * i + 1
right_i = 2 * i + 2 if left_i < n and arr[max_ix] < arr[left_i]:
max_ix = left_i
if right_i < n and arr[max_ix] < arr[right_i]:
max_ix = right_i
if max_ix != i:
arr[max_ix], arr[i] = arr[i], arr[max_ix]
heapify(arr, n, max_ix) def heap_sort(arr):
for i in range(n-1, -1, -1):
heapify(arr, n, i) for i in range(n-1, 0, -1):
arr[i], arr[0] = arr[0], arr[i]
heapify(arr, i, 0)
return arr
线性级排序
此排序方法只适用于数组元素全部为整数的情景。
计数排序
- 找出待排序数组中最大的元素,构造一个长度为此元素值的计数数组。
 - 遍历待排序数组元素,以当前元素为索引,将计数数组中的对应值加1.
 - 此时计数数组中的索引为待排序数组中的元素,值为出现的次数。将计数数组中所有值非0的元素索引根据其出现次数串联起来。
 
def count_sort(arr):
min_ix, max_ix = min(arr), max(arr)
bucket = [0 for _ in range(max_ix+1)]
for i in arr:
bucket[i] += 1
return sum([[i] * bucket[i] for i in range(len(bucket)) if bucket[i] != 0], [])
桶排序
- 设置固定数量的桶(这是个技术活儿).
 - 将待排序数组中的元素放入对应的桶中(对应关系也是个技术活儿,下面的例子中采用整除)
 - 将非空桶中的元素串联起来。
 
def bucket_sort(arr):
min_ix, max_ix = min(arr), max(arr)
bucket_range = (max_ix - min_ix) / len(arr)
# +1 avoid for that max_ix - min_ix will raise a IndexError
temp_bucket = [[] for i in range(len(arr) + 1)]
for i in arr:
temp_bucket[int((i-min_ix)//bucket_range)].append(i)
return sum(temp_bucket, [])
基数排序
- 找出待排序数组中最大元素的位数。将所有元素补足此位数,补足方式为前面补0。
 - 从最低位到最高位,进行多轮数组排序。
 
def radix_sort(arr):
max_value = max(arr)
num_digits = len(str(max_value))
for i in range(num_digits):
bucket = [[] for _ in range(10)]
for j in arr:
bucket[j//(10**i)%10].append(j)
arr = [j for i in bucket for j in i]
return arr
笑死人不偿命排序
睡排序
让多个进程(线程)分别睡眠待排序数组中的元素时长,先睡醒的进程(线程),对应元素追加到结果数组中。
猴子排序
不停随机排序,然后检查是否元素全部有序。如果你是欧皇,那么你可以尝试用这个排序算法,很可能一次搞定。
排序算法复杂度、稳定性及通用性总结
| 算法 | 平均时间复杂度 | 最优时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 是否原地排序 | 是否稳定 | 是否通用 | 
|---|---|---|---|---|---|---|---|
| 冒泡排序 | O(n2) | O(n) | O(n2) | O(1) | 是 | 是 | 是 | 
| 选择排序 | O(n2) | O(n2) | O(n2) | O(1) | 是 | 否 | 是 | 
| 插入排序 | O(n2) | O(n) | O(n2) | O(1) | 是 | 是 | 是 | 
| 希尔排序 | O(n logn) | O(n log2n) | O(n log2n) | O(1) | 是 | 否 | 是 | 
| 归并排序 | O(n logn) | O(n logn) | O(n logn) | O(n) | 否 | 是 | 是 | 
| 快速排序 | O(n logn) | O(n logn) | O(n2) | O(n logn) | 是 | 否 | 是 | 
| 堆排序 | O(n logn) | O(n logn) | O(n logn) | O(1) | 是 | 否 | 是 | 
| 计数排序 | O(n+k) | O(n+k) | O(n+k) | O(k) | 否 | 是 | 否 | 
| 桶排序 | O(n+k) | O(n+k) | O(n2) | O(n+k) | 否 | 是 | 否 | 
| 基数排序 | O(n*k) | O(n*k) | O(n*k) | O(n+k) | 否 | 是 | 否 | 
写在最后
排序算法是算法学习中的核心。掌握排序算法及其思想是学习其它算法的基础。希望大家可以熟练掌握。欢迎关注个人博客:药少敏的博客。
一篇夯实一个知识点系列--python实现十大排序算法的更多相关文章
- 一篇夯实一个知识点系列--python生成
		
写在前面 本系列目的:一篇文章,不求鞭辟入里,但使得心应手. 迭代是数据处理的基石,在扫描内存无法装载的数据集时,我们需要一种惰性获取数据的能力(即一次获取一部分数据到内存).在Python中,具有这 ...
 - 一篇夯实一个知识点系列--python装饰器
		
写在前面 本系列目的:希望可以通过一篇文章,不望鞭辟入里,但求在工程应用中得心应手. 装饰器模式是鼎鼎大名的23种设计模式之一.装饰器模式可以在不改变原有代码结构的情况下,扩展代码功能. Python ...
 - python实现十大核心算法(桶排没实例)
		
# author:sevenduke # 2019-06-11 # 一.交换排序 # 排序算法的温故:冒泡排序 def dubblesort(arr): for i in range(0, len(a ...
 - Python实现的选择排序算法原理与用法实例分析
		
Python实现的选择排序算法原理与用法实例分析 这篇文章主要介绍了Python实现的选择排序算法,简单描述了选择排序的原理,并结合实例形式分析了Python实现与应用选择排序的具体操作技巧,需要的朋 ...
 - 排序算法——(2)Python实现十大常用排序算法
		
上期为大家讲解了排序算法常见的几个概念: 相关性:排序时是否需要比较元素 稳定性:相同元素排序后是否可能打乱 时间空间复杂度:随着元素增加时间和空间随之变化的函数 如果有遗忘的同学可以看排序算法——( ...
 - Python实现十大经典排序算法(史上最简单)。
		
十大排序算法(Python实现)一. 算法介绍及相关概念解读 算法分类十种常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn), ...
 - Python实现十大经典排序算法(史上最简单)
		
十大排序算法(Python实现)一. 算法介绍及相关概念解读 算法分类十种常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn), ...
 - 每日一个知识点系列:volatile的可见性原理
		
每日一个知识点系列的目的是针对某一个知识点进行概括性总结,可在一分钟内完成知识点的阅读理解,此处不涉及详细的原理性解读. img 看图说话 关键点1: 总线嗅探器(MESI 缓存一致性原理 ) 关键点 ...
 - python实现十大经典排序算法
		
Python实现十大经典排序算法 代码最后面会给出完整版,或者可以从我的Githubfork,想看动图的同学可以去这里看看: 小结: 运行方式,将最后面的代码copy出去,直接python sort. ...
 
随机推荐
- Linux 通过源代码安装和编译程序
			
Linux源代码安装在平常工作学习中经常用到,总结下步骤↓↓↓ 第一步:#mount /dev/cdrom/mnt (挂载一个软盘) 第二步:手动安装httpd-2.4.25.tar.gz 依赖关系包 ...
 - 德布鲁因序列与indexing 1
			
目录 写在前面 标记left-most 1与right-most 1 确定位置 德布鲁因序列(De Bruijn sequence) 德布鲁因序列的使用 德布鲁因序列的生成与索引表的构建 参考 博客: ...
 - (4)webpack中配置css,scss,less第三方loader
			
为什么要使用第三方loader 一般引入样式文件,我们会在html中引入样式标签. 很明显,这样就跟之前在script中引入js文件一样,会导致二次请求.我们希望webpack像处理js文件一样处理样 ...
 - db2数据库字段更新当前时间
			
db2数据库中想要将字段的时间通过sql语句的方式更新: 例如: Update tablename set 字段1='打酱油', 字段2 = TO_CHAR(current timestamp,'YY ...
 - MemoryCacheHelper与RedisCacheHelper缓存集成与测试笔记
			
因为每次在新项目中需要花费大量时间在基础类库搬移.调试.为了节省时间(偷懒)就将MemoryCacheHelper/RedisHelper进行了封装 本次是关于缓存方面记录,源码请参考(包含Redis ...
 - 简单实用的办公软件导航网站,IT经理必备工具
			
最近非常忙,因为公司上线了业财一体化系统.今天分享一个非常实用的办公软件导航网站,节省了我很多百度的时间. 快氪导航,让软件服务更简单. 一.办公软件导航 站长已经按照功能进行了分类:协同办公,流程审 ...
 - Python API 操作Hadoop hdfs详解
			
1:安装 由于是windows环境(linux其实也一样),只要有pip或者setup_install安装起来都是很方便的 >pip install hdfs 2:Client——创建集群连接 ...
 - 我在 GitHub 上发现了一款骚气满满的字体!
			
本文转自量子位,作者栗体,如有侵权,则可删除. github字体 这个字体叫 Leon Sans,表面看去平平无奇. 但事实上,它并不是普通的字体,体内蕴藏着魔力. github字体1 Leon Sa ...
 - Java基础(一)基础常识
			
Java开发基础流程图: 也可参考这篇博客: https://www.cnblogs.com/xdp-gacl/p/3624567.html 常用的Windows的DOS命令 : dir : 列出当前 ...
 - PHP array_search() 函数
			
实例 在数组中搜索键值 "red",并返回它的键名: <?php$a=array("a"=>"red","b" ...