目录
文章开头先给大家推荐一个学习算法的一个非常不错的网站:Hello算法。相信很多小伙伴都听说过并且也在该网站学习过算法。
《Hello 算法》是一本开源免费、新手友好的数据结构与算法入门书。
- 全书采用动画图解,引导初学者探索数据结构与算法的“知识地图”,为算法学习与刷题打下坚实基础;
- 源代码皆可一键运行,使读者在实践练习中提升编程技能,了解数据结构与算法的底层实现和工作原理;
Hello 算法Github代码仓库:https://github.com/krahets/hello-algo
Hello 算法网页版:https://www.hello-algo.com/chapter_preface/
多语言支持
目前已将代码译写至 Java, C++, Python, Go, JavaScript, TypeScript, C#, Swift, Zig 等多种语言,可满足大多数小伙伴需要。
内容结构
该版本共包含 12 章内容,写在前面、引言、复杂度分析、数据结构简介、数组与链表、栈与队列、散列表、树、堆、图、查找算法、排序算法、附录。
但是下文中的代码都是我自己以前根据不同排序算法思路写的,呢时候还不知道有这个网站,这个网站也是我的一个师兄告诉我的,真心觉得不错,所以推荐给大家。所以我不是照搬该网站里面的代码,当然我自己写的肯定不如人家网站里面写得好。
在计算机程序中,排序是基本且至关重要的操作。从搜索引擎优化结果列表、数据分析、至数据库查询优化,几乎所有的技术系统都以某种形式依赖于有效的排序机制。排序算法的效率直接影响了程序的性能,特别是在处理大数据集时。因此,了解和实现常用的排序算法不仅是一个重要的学术练习,同时也是实践编程技能的核心。
排序算法可以根据不同的指标来分类:它们的执行时间(时间复杂度)、它们在执行中需要的额外空间(空间复杂度)、算法的稳定性(是否保持相等元素的初始顺序),以及它们是递归地还是迭代地执行任务。这些指标帮助我们决定在具体应用中选择哪一个算法。例如,一个内存敏感的应用可能需要一个空间复杂度低的排序算法,而在对速度要求极高的系统中,选择一个时间复杂度低的排序算法是至关重要的。
在本篇博客中,我们将深入探讨几种常用的排序算法,并提供它们的Python代码实现,以供所有级别的编程爱好者学习和使用。
1.冒泡排序
冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
代码示例:
# 从输入中获取一组整数,并将其转换为列表
b = list(map(int, input().split(' ')))
# 获取列表的长度
n = len(b)
# 对列表进行冒泡排序
for i in range(1, n):
# 对每个元素进行比较
for j in range(n - i):
# 如果当前元素大于下一个元素
if b[j] > b[j + 1]:
# 交换两个元素的位置
t = b[j]
b[j] = b[j + 1]
b[j + 1] = t
# 打印排序后的列表
print(b)
改进的冒泡排序
这个改进的版本在每一轮遍历后都会检查是否已经完成排序,如果已经完成则提前结束,从而提高效率。
代码示例:
# 从输入中获取一组整数,并将其转换为列表
b = list(map(int, input().split(' ')))
# 获取列表的长度
n = len(b)
# 初始化一个标志位,用于标记是否已经完成排序
flag = 1
# 对列表进行冒泡排序
for i in range(1, n):
# 如果在上一轮遍历中没有发生交换,说明已经完成排序,可以提前结束
if flag == 1:
flag = 0
# 对每个元素进行比较
for j in range(n - i):
# 如果当前元素大于下一个元素
if b[j] > b[j + 1]:
# 标记发生了交换
flag = 1
# 交换两个元素的位置
t = b[j]
b[j] = b[j + 1]
b[j + 1] = t
# 打印排序后的列表
print(b)
2.堆排序
堆排序是一种基于比较的排序算法,它将待排序的序列视为一个完全二叉树,然后通过调整节点的位置,使得每个节点都大于或等于其子节点,形成一个大顶堆。然后将堆顶元素与最后一个元素交换,然后对剩余的元素重新调整为大顶堆,如此反复,直到所有元素都被排序。
代码示例:
# 从输入中获取一组整数,并将其转换为列表
b = list(map(int, input().split(' ')))
# 初始化一个列表a,第一个元素为0,其余元素为b的元素
a = [0]
for i in b:
a.append(i)
# 定义堆调整函数,s为要调整的节点,m为堆的大小
def HeapAjust(a, s, m):
# rc为要调整的节点的值
rc = a[s]
# j为s的左子节点
j = 2 * s
# 如果左子节点在堆内,则进入循环
while (j <= m):
# 如果右子节点在堆内,且右子节点的值大于左子节点的值,则j指向右子节点
if j < m and a[j] < a[j + 1]:
j += 1
# 如果要调整的节点的值大于等于j节点的值,则退出循环
if rc >= a[j]:
break
# 否则,将j节点的值赋给s节点,然后s指向j节点,j指向s的左子节点
a[s] = a[j]
s = j
j *= 2
# 将rc的值赋给s节点
a[s] = rc
# 定义堆排序函数,n为要排序的元素个数
def HeapSort(a, n):
# 从最后一个非叶子节点开始,对每个节点进行堆调整
for i in range(n // 2, 0, -1):
HeapAjust(a, i, n)
# 从最后一个元素开始,将堆顶元素与最后一个元素交换,然后对剩余的元素进行堆调整
for i in range(n, 1, -1):
t = a[1]
a[1] = a[i]
a[i] = t
HeapAjust(a, 1, i - 1)
# 如果是主程序,则调用堆排序函数,然后打印排序后的结果
if __name__ == '__main__':
HeapSort(a, len(a) - 1)
print(a)
# 打印排序后的结果,从第二个元素开始打印,因为第一个元素是0
for i in range(1, len(a)):
print(a[i], end=' ')
3.希尔排序
希尔排序是一种基于插入排序的算法,通过比较相距一定间隔的元素,我们可以一次交换一对元素,它们可能有很长的距离,然后会导致数据移动到更接近它最终排序位置。希尔排序是非稳定排序算法。
代码示例:
# 从输入中获取一组整数,并将其转换为列表
b = list(map(int, input().split(' ')))
# 初始化一个列表a,第一个元素为0,其余元素为b的元素
a = [0]
for i in b:
a.append(i)
# 定义希尔排序的增量序列
data = [5, 3, 1]
# 定义希尔排序函数,a为要排序的列表,dk为当前的增量
def xier_sort(a, dk):
# i从dk+1开始,因为前dk个元素默认为已排序的
i = dk + 1
# 如果i小于列表的长度,则进入循环
while (i < len(a)):
# 如果当前元素小于它前面的元素
if a[i] < a[i - dk]:
# 将当前元素的值保存到a[0]
a[0] = a[i]
# j为当前元素前面的元素
j = i - dk
# 如果j大于0,则进入循环
while (j > 0):
# 如果a[0]小于a[j]
if a[0] < a[j]:
# 将a[j]的值赋给a[j+dk]
a[j + dk] = a[j]
# j减去dk
j = j - dk
else:
break
# 将a[0]的值赋给a[j+dk]
a[j + dk] = a[0]
# i加1
i = i + 1
# 返回排序后的列表
return a
# 对每个增量,调用希尔排序函数
for dk in data:
a = xier_sort(a, 1)
# 打印排序后的结果,从第二个元素开始打印,因为第一个元素是0
print(a)
for i in range(1, len(a)):
print(a[i], end=' ')
4.快速排序
快速排序是一种高效的排序算法,它使用分治法(Divide and Conquer)策略来把一个序列(list)分为两个子序列(sub-lists),步骤是:
- 从序列中挑出一个元素,作为"基准"(pivot),
- 重新排序序列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作,
- 递归地(recursive)把小于基准值元素的子序列和大于基准值元素的子序列排序。
代码示例:
# 从输入中获取一组整数,并将其转换为列表
b = list(map(int, input().split(' ')))
# 初始化一个列表a,第一个元素为0,其余元素为b的元素
a = [0]
for i in b:
a.append(i)
# 定义快速排序函数,low为排序开始的位置,high为排序结束的位置
def QSort(a, low, high):
if low < high:
# 找出基准的位置
pivotloc = Partition(a, low, high)
# 对基准左边的序列进行快速排序
QSort(a, low, pivotloc - 1)
# 对基准右边的序列进行快速排序
QSort(a, pivotloc + 1, high)
# 定义分区函数,low为排序开始的位置,high为排序结束的位置
def Partition(a, low, high):
# 将基准值保存在a[0]
a[0] = a[low]
pivotkey = a[low]
while (low < high):
# 从右向左找到第一个小于基准的值
while (low < high and a[high] >= pivotkey):
high -= 1
# 将这个值移动到左边
a[low] = a[high]
# 从左向右找到第一个大于基准的值
while (low < high and a[low] <= pivotkey):
low += 1
# 将这个值移动到右边
a[high] = a[low]
# 将基准值放到正确的位置
a[low] = a[0]
# 返回基准的位置
return low
# 如果是主程序,则调用快速排序函数,然后打印排序后的结果
if __name__ == '__main__':
QSort(a, 1, len(a) - 1)
print(a)
# 打印排序后的结果,从第二个元素开始打印,因为第一个元素是0
for i in range(1, len(a)):
print(a[i], end=' ')
5.二分插入排序
二分插入排序是一种改进的插入排序,它在插入元素时使用二分查找的方法找到元素的插入位置,从而减少了比较的次数,提高了排序的效率。
代码示例:
# 从输入中获取一组整数,并将其转换为列表
yuanshi = list(map(int, input().split(' ')))
# 初始化一个列表shaobing,第一个元素为0,其余元素为yuanshi的元素
shaobing = [0]
for i in yuanshi:
shaobing.append(i)
yuanshi = shaobing
print(yuanshi)
# 从第五个元素开始,对每个元素进行二分插入排序
for i in range(5, len(yuanshi)):
# 将当前元素的值保存到yuanshi[0]
yuanshi[0] = yuanshi[i]
# 初始化low和high
low = 1
high = i - 1
# 使用二分查找找到元素的插入位置
while (low <= high):
mid = (low + high) // 2
if (yuanshi[0] < yuanshi[mid]):
high = mid - 1
else:
low = mid + 1
# 将元素插入到正确的位置
for j in range(i - 1, high, -1):
yuanshi[j + 1] = yuanshi[j]
yuanshi[high + 1] = yuanshi[0]
# 打印排序后的结果
print(yuanshi)
6.简单选择排序
简单选择排序是一种基本的排序算法,这种算法首先在未排序的序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
代码示例:
# 从输入中获取一组整数,并将其转换为列表
b = list(map(int, input().split(' ')))
# 对列表进行简单选择排序
for i in range(len(b) - 1):
# 初始化最小元素的位置
k = i
# 从未排序的元素中找到最小元素的位置
for j in range(i + 1, len(b)):
if b[j] < b[k]:
k = j
# 如果最小元素的位置不是i,交换b[i]和b[k]
if k != i:
t = b[i]
b[i] = b[k]
b[k] = t
# 打印排序后的列表
print(b)
# 以空格分隔的方式打印排序后的列表
for i in b:
print(i, end=' ')