Python实现十大经典排序 I
摘要: 本博客主要介绍冒泡排序、选择排序、插入排序、快速排序、归并排序等五种排序方法思想及Python实现。
版本号 | 更新时间 | 作者 | 更新章节 | 更新概述 |
V1.0 | 2019-01-14 | 蒙蕤 | 1-5 | 算法描述、Python实现 |
V1.1 | 2019-01-16 | 蒙蕤 | 1-5 | 代码注释 |
1.冒泡排序
在排序算法中,冒泡排序相对容易理解和编码。其策略是从列表的开头处开始,比较一对数据项,直到移动到列表的末尾。每当成对的两项之间顺序不正确时,交换其位置。这个过程的效果就是将最大的项以冒泡的方式排序到列表的末尾。然后,算法从列表开头到倒数第2项重复这一过程,依次类推,直到该算法从列表的最后一项执行。此时列表已排序好。以下用表格展示这一过程,示例用5个元素的列表:
未排序列表 | 第一次比较 | 第二次比较 | 第三次比较 | 第四次比较 |
---|---|---|---|---|
5 | 1* | 1 | 1 | 1 |
1 | 5* | 2* | 2 | 2 |
2 | 2 | 5* | 4* | 4 |
4 | 4 | 4 | 5* | 3* |
3 | 3 | 3 | 3 | 5* |
冒泡排序存在循环嵌套,对于大小为n的列表,内部的循环执行次,其复杂度为O()。
冒泡排序的Python实现:
def bubble_sort(lyst):
n = len(lyst)
while n > 1:
# swapped 标记在本次 while 循环中是否出现元素交换
# 如果没有交换,则说明列表已经排序好,直接return即可
# 优化最坏的情况复杂度
swapped = False
i = 1 # 开始位置,每次都从第一个元素往后比较
while i < n: #
if lyst[i] < lyst[i-1]:
swapped = True
lyst[i], lyst[i-1] = lyst[i-1], lyst[i]
i += 1
if not swapped:
return # no swaps so that lyst sorted
n -= 1 # 最大项已放末尾,下次循环只需比较该位置之前元素
2.选择排序
顾名思义,从列表中找到最小项,如果其位置不是列表的第一项,则交换两个位置的项。然后算法从第二个位置开始执行该过程,直到算法到达整个过程的最后一项。以下用表格展示该过程:
选择排序的时间复杂度与冒泡排序一样。
选择排序的Python实现:
def select_sort(lyst):
for i in range(0, len(lyst)-1):
min_index = i # 最小元素下标,先定义待排序位置最小
for j in range(i, len(lyst)):
if lyst[j] < lyst[min_index]:
min_index = j # 更新最小元素下标
if min_index != i: # 交换需排序位置元素及最小元素
lyst[min_index], lyst[i] = lyst[i], lyst[min_index]
3. 插入排序
插入排序的策略如下:
在第 i 轮通过列表的时候(i 为 1 到 n - 1),第 i 个项应该插入到列表的前 i 个项之中的正确位置。
1.第 i 轮之后,前 i 个项应该是排序好的。
2.其时间复杂度依然是O()。
插入排序的Python实现:
def insertion_sort(lyst):
# 从第2个元素开始循环,之前只有一个元素,肯定顺序正确
for i in range(1, len(lyst)):
item_to_insert = lyst[i] # 待插入元素
for j in range(i-1, -1, -1): # 从第 i-1 往前循环,直到遇到比带插入元素小的
if item_to_insert < lyst[j]:
lyst[j + 1] = lyst[j]
else:
# 前面元素都比待插入元素小
break
lyst[j+1] = item_to_insert
4.快速排序
快速排序采取分而治之的策略,将列表分解成更小的子列表,随后,这些子列表再递归地排序。对于每个小的列表迭代比较计算次数肯定小的多,所以相较于n*n的循环嵌套,算法复杂度肯定更优越。具体方法如下:
1.首先,从列表的中间位置选取一项作为基准点
2.将列表中的项按基准点分区,使小于基准点的项位于基准点的左侧,大于基准点的项位于基准点右侧。
3.分而治之,对于子列表,重新选择基准点重复过程。
算法复杂度计算:
分割次数:,
x次分割计算:经过 x 次分割子列表数量为 个,每个子列表元素个数为 个,则需要计算比较
总计算比较次数:
所以算法复杂度为。
快速排序的Python实现:
def quit_sort(lyst):
quit_sort_helper(lyst, 0, len(lyst) - 1)
def quit_sort_helper(lyst, left, right):
"""
排序方法
递归
也是数学归纳法的体现:如果 n=1, n=2成立,则一定能够n=k时成立。
"""
if left < right:
pivot_location = partition(lyst, left, right)
quit_sort_helper(lyst, left, pivot_location-1)
quit_sort_helper(lyst, pivot_location+1, right)
def partition(lyst, left, right):
"""
将列表 left 至 right 之间的元素按基准点分开
本算法不需要注重局部顺序,因为当分割至2个元素时肯定能够排序好
"""
middle = left + (right - left) // 2 # 用减法计算中间,防止溢出变负
pivot = lyst[middle] # 取中间元素作为基准点
lyst[middle], lyst[right] = lyst[right], pivot # 首先将基准点放至最末端
boundary = left # 分界,最后也是基准点需要插入的坐标
for index in range(left, right):
if lyst[index] < pivot:
lyst[index], lyst[boundary] = lyst[boundary], lyst[index]
boundary + 1
lyst[right], lyst[boundary] = lyst[boundary], lyst[right]
5.归并排序
归并排序的算法思想与快速排序类似,算法复杂度为。其策略如下:
1.计算列表的中间位置,并递归地排序其左边和右边的子列表。
2.将两个排序好的子列表重新合并为单个排序好的列表
3.当子列表不能再划分的时候,停止这个过程。
归并排序Python实现:
def merge_sort(lyst):
"""
归并排序函数
传入 lyst , 函数运行结束,则 lyst 被排序好
列表为可变对象,可以在函数中直接改变lyst
"""
# copy_buffer 为额外消耗的内存空间
# 为了节约分配空间的资源消耗,借助python可变对象的特性,首先初始化一个缓存列表
# 其他函数中重复使用该缓存空间
copy_buffer = [None for _ in lyst]
merge_sort_helper(lyst, copy_buffer, 0, len(lyst) - 1)
def merge_sort_helper(lyst, copy_buffer, low, high):
"""
递归思想
"""
if low < high:
middle = low + (high - low) // 2
merge_sort_helper(lyst, copy_buffer, low, middle)
merge_sort_helper(lyst, copy_buffer, middle + 1, high)
merge(lyst, copy_buffer, low, middle, high)
def merge(lyst, copy_buffer, low, middle, high):
"""
合并两个排序好的列表
两个子列表: low----middle, middle+1----high
"""
i1, i2 = low, middle + 1
for i in range(low, high + 1):
if i1 > middle: # 第一个子列表已完成
copy_buffer[i] = lyst[i2]
i2 += 1
elif i2 > high: # 第二个子列表已完成
copy_buffer[i] = lyst[i1]
i1 += 1
elif lyst[i1] < lyst[i2]: # i1 较小
copy_buffer[i] = lyst[i1]
i1 += 1
else: # i2 较小
copy_buffer[i] = lyst[i2]
i2 += 1
for i in range(low, high+1):
# 将已经排序好的 缓存列表元素赋值给 lyst 对应位置
lyst[i] = copy_buffer[i]
merge函数是将两个排序好的子列表合并到一个大的排序好的子列表中。第1子列表在low和middle之间,第2个子列表再middle+1和high之间。该函数包含三个步骤:
1.将索引指针设置为每个子列表的第一项。分别为low和middle+1
2.从每个子列表的第1项开始,重复的比较,将较小的项从其子列表中复制到复制缓存中,并且继续处理子列表中的下一项。重复这个过程直到两个列表中的项都复制过了。如果先到达了其中一个子列表的末尾,通过从另一个子列表复制剩余的项结束该步骤。
3.将copy_buffer中low和high之间的部分,复制回lyst中对应的位置处。
由此可见,合并排序空间复杂度为O(n)。
此博客提到的五种排序算法(冒泡排序、选择排序、插入排序、快速排序、归并排序)为基本排序算法。后面会继续写一篇博客来介绍希尔排序、堆排序、桶排序、基数排序、计数排序等五种算法思想及Python实现。
---------------------
作者:蒙蕤
来源:CSDN
原文:https://blog.csdn.net/Mengrui_1991/article/details/86479852
版权声明:本文为博主原创文章,欢迎交流。如需转载,请附上博文链接。