十大经典排序算法(python实现)
- 知识站点
- 时间复杂度
- 1冒泡排序
- 稳定性
- 简介
- 步骤
- 可视化图
- 代码实现
- 2选择排序
- 稳定性
- 简介
- 步骤
- 可视化图
- 代码实现
- 3插入排序
- 稳定性
- 简介
- 步骤
- 可视化图
- 代码实现
- 4希尔排序
- 稳定性
- 简介
- 可视化图
- 代码实现
- 5归并排序
- 稳定性
- 简介
- 可视化图
- 步骤
- 代码实现
- 6快速排序
- 稳定性
- 适用场景
- 步骤
- 可视化图
- 代码实现
知识站点
时间复杂度
什么是时间复杂度呢?举个例子先让我们来想象一个场景:某一天,小黄和小蓝同时到一个公司应聘,他们分不出胜负。面试官给他们出了一道题目,保存员工编号。时间一到小黄和小蓝各自交付了代码,两端代码实现的功能都差不多。小黄的代码运行一次要花100豪秒,内存占用5MB。小蓝的代码运行一次要花100秒,内存占用500MB。
我,我们可知由此可见,衡量代码的好坏,有两个指标:
1.运行时间
2.占用空间
从现实的四件事来做比喻:
1,给小蓝一条长10寸的面包,小蓝每3天吃掉1寸,那么吃掉整个面包需要几天?
我们肯定会脱口而出:3 x 10 = 30(天)
如果面包的长度是 X 寸呢?
此时吃掉整个面包,需要 3 X n = 3n 天。
如果用一个函数来表达这个相对时间,可以记作 T(n)=3n。
2,给小蓝一条长16寸的面包,小蓝每5天吃掉面包剩余长度的一半,第一次吃掉8寸,第二次吃掉4寸,第三次吃掉2寸…那么小蓝把面包吃得只剩下1寸,需要多少天呢?
这个问题翻译一下,就是数字16不断地除以2,除几次以后的结果等于1?这里要涉及到数学当中的对数,以2位底,16的对数,可以简写为log(16)。
因此,把面包吃得只剩下1寸,需要 5 X log(16) = 5 X 4 = 20 天。
如果面包的长度是 N 寸呢?
需要 5 X logn =(5)logn天,记作 T(n)= (5)logn。
3,给蓝一条长10寸的面包和一个鸡腿,小蓝每2天吃掉一个鸡腿。那么小蓝吃掉整个鸡腿需要多少天呢?
答案自然是2天。因为只说是吃掉鸡腿,和10寸的面包没有关系 。
如果面包的长度是N寸呢?
无论面包有多长,吃掉鸡腿的时间仍然是2天,记作T(n)=2。
4,给小蓝一条长10寸的面包,小蓝吃掉第一个一寸需要1天时间,吃掉第二个一寸需要2天时间,吃掉第三个一寸需要3天时间…每多吃一寸,所花的时间也多一天。那
么小灰吃掉整个面包需要多少天呢?
答案是从1累加到10的总和,也就是55天。
如果面包的长度是 N 寸呢?
此时吃掉整个面包,需要 1+2+3+…+ n-1 + n = (1+n)*n/2 = 0.5n^2 + 0.5n。
记作T(n) = 0.5n^2 + 0.5n。
时间:
场景1:T(n) = 3n,执行次数是线性的。
场景2:T(n)= (5)logn,执行次数是对数的。
场景3:T(n)=2,执行次数是常量的。
场景4:T(n) = 0.5n^2 + 0.5n,执行次数是一个多项式。
1冒泡排序
稳定性
冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。
所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。
简介
冒泡排序是一种非常简单的排序方法。一直重复地检查数列直到顺序正确,每次检查都比较两个元素来经行对比,如果它前面比后面大就把它们交换,直到排序成功。
步骤
- 比较相邻的元素。如果第一个比第二个大,就交换它们两个 的位置
- 对每一对相邻元素作同样的移动,从开始第一个到结尾的最后一个,这样在最后的元素是最大的数
- 对所有的元素重复1~2步骤,除了最后一个
- 重复步骤1~3,直到排序完成。
可视化图
代码实现
def bubble_sort(data):
for j in range(len(data) + 1):
for i in range(len(data) - (j + 1)):
if data[i] > data[i + 1]:
tmp = data[i + 1]
data[i + 1] = data[i]
data[i] = tmp
print(data)
2选择排序
稳定性
选择排序稳定性不是很好,因为选择排序有一个弊端就是数字越多就有更大的可能
简介
从头至尾扫描数列,找出最小的一个元素,和第一个元素交换,接着从剩下的元素中继续这种选择和交换方式,最终得到一个有序数列
步骤
- 首先在未排序序列中找到最小(大)元素
- 存放到排序序列的起始位置
- 再从剩余未排序元素中继续寻找最小(大)元素
- 然后放到已排序序列的末尾
- 重复步骤1~3,直到排序完成
可视化图
代码实现
def quick_sort(ser):
if len(ser) < 2:
return
p = ser[0]
L = []
E = []
R = []
while len(ser) > 0:
if ser[-1] < p:
L.append(ser.pop())
elif ser[-1] == p:
E.append(ser.pop())
else:
R.append(ser.pop())
quick_sort(L)
quick_sort(R)
ser.extend(L)
ser.extend(E)
ser.extend(R)
if __name__ == '__main__':
ser = [1, 7, 3, 5, 4]
quick_sort(ser
print(ser)
3插入排序
稳定性
插入排序的稳定性比较好因为碰见一个和插入元素相 等的,那么插入元素把想插入的元素放在相等元素的后面
简介
插入排序是一种很简单的排序算法,工作原理是通过创建有序数列,对于未排序数列,在已排序数列中从后向前扫描,找到相应位置并插入。
步骤
- 从第一个元素开始,该元素可以认为已经被排序
- 取出下一个元素,在已经排序的元素序列中从后向前扫描
- 如果该元素(已排序)大于新元素,将该元素移到下一位置
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
- 将新元素插入到该位置后
- 重复步骤2~5
可视化图
代码实现
def insert_Sort(la):
length = len(la)
for i in range(1, length):
x = la[i]
for j in range(i, -1, -1):
if x < la[j - 1]:
la[j] = la[j - 1]
else:
break
la[j] = x
print(la)
4希尔排序
稳定性
由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以希尔排序是不稳定的。
简介
是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。
可视化图
代码实现
def donald_shell(data):
n = len(data)
gap = n // 2
while gap >= 1:
for j in range(gap, n):
while j - gap >= 0:
if data[j] < data[j - gap]:
data[j], data[j - gap] = data[j - gap], data[j]
j -= gap
else:
break
gap //= 2
print(data)
5归并排序
稳定性
因为我们在遇到相等的数据的时候必然是按顺序“抄写”到数组上的,所以,归并排序同样是稳定算法。
简介
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
可视化图
步骤
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
- 重复步骤3直到某一指针到达序列尾
- 将另一序列剩下的所有元素直接复制到合并序列尾
代码实现
def merge(a, b):
c = []
h = j = 0
while j < len(a) and h < len(b):
if a[j] < b[h]:
c.append(a[j])
j += 1
else:
c.append(b[h])
h += 1
if j == len(a):
for i in b[h:]:
c.append(i)
else:
for i in a[j:]:
c.append(i)
return c
def merge_sort(lists):
if len(lists) <= 1:
return lists
middle = len(lists) / 2
left = merge_sort(lists[:middle])
right = merge_sort(lists[middle:])
return merge(left, right)
6快速排序
稳定性
快速排序并不是稳定的。这是因为我们无法保证相等的数据按顺序被扫描到和按顺序存放。
适用场景
快速排序在大多数情况下都是适用的,尤其在数据量大的时候性能优越性更加明显。但是在必要的时候,需要考虑下优化以提高其在最坏情况下的性能。
步骤
- 首先在未排序序列中找到最小(大)元素
- 存放到排序序列的起始位置
- 再从剩余未排序元素中继续寻找最小(大)元素
- 然后放到已排序序列的末尾
- 重复步骤1~3,直到排序完成
可视化图
代码实现
def quick_sort(data):
# 递归入口及出口
if len(data) >= 2:
# 选取基准值,也可以选取第一个或最后一个元素
mid = data[0]
# 定义基准值左右两侧的列表
left, right = [], []
# 从原始数组中移除基准值
data.remove(mid)
#
for num in data:
if num >= mid:
right.append(num)
else:
left.append(num)
return quick_sort(left) + [mid] + quick_s
这是本人做的一些算法总结不喜勿喷