这里主要讲各个排序的思想 原理 和其时间 空间复杂度
代码什么的网上都有 CV一下就行了
主要讲冒泡排序 直接插入排序 直接选择排序 快速排序
其他排序不方便直接用文字讲解(我不会画图) 推荐数形结合
推荐网站:图码
数据结构可视化交互动画版 | 图码图码是一个数据结构和算法可视化交互动画版网站,皆在创建一个可以直观地学习编程、数据结构和算法的平台。囊括国内计算机考研、期末考试、408数据结构和面试的常见算法题。打造一个学习的新平台新社区。https://www.totuma.cn/
冒泡排序是一种简单的排序算法,基于 交换相邻元素 的思想,它通过重复地遍历待排序的元素,逐步将较大的元素“冒泡”到序列的末端。它的核心思想可以用“相邻元素两两比较并交换”的方式来理解。
最坏情况:当序列是反向排列时,每次遍历都需要进行元素交换。此时,冒泡排序的时间复杂度为 O(n^2)。
最好情况:当序列已经是有序的时,冒泡排序只需要进行一趟遍历,并且没有发生任何交换。此时,时间复杂度是 O(n)(可以通过设置一个标志来优化,若一趟遍历没有交换元素,则提前结束排序)。
平均情况:通常为 O(n^2),因为每对元素都要进行比较。
空间复杂度:冒泡排序是一种 原地排序算法,它只需要常数空间用于存储临时变量,因此空间复杂度是 O(1)。
稳定
直接插入排序是一种简单的排序算法,其基本思想与我们日常插入牌类游戏中的操作类似:在给扑克牌进行整理的时候 都会将其中一张牌先整理 然后在选择下一张牌有序地插入前一个数组中
假设有一组数据:
[23,54,22,56,1,45,67,6]
step1
首先将第一个数进行排序:
[23] [54,22,56,1,45,67,6]
现在23已经排好序了(一个元素肯定是有序的)
step2
接下来选择无序列中的第一个元素54
将其插入到有序列中
[23,54] [22,56,1,45,67,6]
stepN
重复上述操作
[22,23,54] [56,1,45,67,6]
[22,23,54,56] [1,45,67,6]
[1,22,23,54,56] [45,67,6]
[1,22,23,45,54,56] [67,6]
[1,22,23,45,54,56,67] [6]
[1,6,22,23,45,54,56,67] [ ]
这样就完成了排序
时间复杂度:O(n^2)
最坏情况(逆序排列):每次插入都需要移动所有已经排好序的元素,时间复杂度为 O(n^2)。
最好情况(已排序):每次插入都不需要移动元素,时间复杂度为 O(n)。
平均情况:时间复杂度通常为 O(n^2)。
空间复杂度:直接插入排序是一种 原地排序算法,它只需要常数空间来存储临时变量,因此空间复杂度是 O(1)。
稳定
直接选择排序是一种简单的排序算法,其核心思想是每次从待排序的元素中选出最小(或最大)元素,将其与当前待排序区间的第一个元素交换,逐步缩小待排序区间,直到所有元素都排好序。
直接选择排序的基本思想:
1. 从未排序部分中选择最小元素:每一轮排序,选择当前未排序部分中最小的元素。
2. 与当前元素交换位置:将选出的最小元素与当前待排序区间的第一个元素交换位置,确保该元素已经排好序。
3. 逐步缩小排序区间:每一轮排序都会使得一个元素排好序,直到整个序列排好序。
假设有一组数据:
[23,54,22,56,1,45,67,6]
step1
在列中找到最小的元素 与第一个元素进行交换
[23,54,22,56,1,45,67,6]
将23和1进行交换
[1,54,22,56,23,45,67,6]
现在1是有序的了
step2
接下来选择第二小的元素与54交换
[1,54,22,56,23,45,67,6]
[1,6,22,56,23,45,67,54]
现在1,6是有序的了
stepN
重复以上步骤
[1,6,22,56,23,45,67,54]
[1,6,22,56,23,45,67,54] //22本身是无序列中最小的了 无需交换
[1,6,22,56,23,45,67,54]
[1,6,22,23,56,45,67,54]
[1,6,22,23,56,45,67,54]
[1,6,22,23,45,56,67,54]
[1,6,22,23,45,56,67,54]
[1,6,22,23,45,54,67,56]
[1,6,22,23,45,54,67,56]
[1,6,22,23,45,54,56,67]
至此排序完成
最坏情况:每次选择最小元素都需要遍历剩下的所有元素,时间复杂度为 O(n^2)。
最好情况:即使元素已经有序,选择排序也会进行完整的比较,因此时间复杂度仍然是 O(n^2)。
平均情况:时间复杂度通常为 O(n^2)。
空间复杂度:直接选择排序是一种 原地排序算法,只需要常数空间来存储临时变量,因此空间复杂度是 O(1)。
不稳定
交换会打乱原来相同元素的前后顺序
快速排序是一种高效的排序算法,其基本思想是 分治法。通过选择一个”基准(枢轴量)”元素,将待排序的数组分为两个子数组:一个子数组中所有元素都比基准元素小,另一个子数组中所有元素都比基准元素大。然后递归地对这两个子数组进行排序。
快速排序的基本思想:
把比枢轴量小的元素丢到枢轴量前面
把比枢轴量大的元素丢到枢轴量后面
1. 选择基准(枢轴量)元素:从待排序的数组中选择一个元素作为“基准”元素(通常是第一个元素、最后一个元素、或者中间元素)。
2. 分割数组:通过一趟排序将数组重新排列,确保基准元素左侧的所有元素都比基准元素小,右侧的所有元素都比基准元素大。
3. 递归排序子数组:对基准元素左边和右边的子数组分别递归进行快速排序,直到每个子数组的长度为 1 或 0,排序完成。
假设有一组数据:
[23,54,22,56,1,45,67,6]
step1
以23为枢轴量
将23放到哨兵位置进行保存 //相当于temp
将low指针移动到23这个元素
将high指针移动到6这个元素
发现low>high
将high赋值给low
23 [6,54,22,56,1,45,67,6]
移动low指针:low+1
现在low指向54
发现low>high
将high赋值给low
23 [6,54,22,56,1,45,67,54]
移动high指针:high-1
现在high指向67
发现high>枢轴量
pass
移动high指针:high-1
现在high指向45
发现high>枢轴量
pass
移动high指针:high-1
现在high指针指向1
发现high<枢轴量
将high赋值给low
23 [6,1,22,56,1,45,67,54]
移动low指针:low+1
现在low指向22
发现low<枢轴量
pass
移动low指针:low+1
现在low指向56
发现low>枢轴量
将low赋值给high
23 [6,1,22,56,56,45,67,54]
移动high指针:high-1
现在high指针指向56
发现low=high
将枢轴量放入low中
23 [6,1,22,23,56,45,67,54]
至此 第一趟排序结束
接下来递归地进行上述步骤即可
快速排序伪代码
quickSort(arr):
if len(arr) <= 1:
return arr
pivot = arr[0] # 或选择其他方法确定枢轴量
left = [x for x in arr[1:] if x < pivot]
right = [x for x in arr[1:] if x >= pivot]
return quickSort(left) + [pivot] + quickSort(right)
最坏情况(基准选择不当,导致极端分割):当数组已经是有序的,或者选择的基准元素总是最大或最小元素时,时间复杂度为 O(n^2)。
最好情况(基准元素将数组平分为两部分):每次都将数组分成两个相等的子数组,时间复杂度为 O(n log n)。
平均情况:如果基准元素选择得较好,快速排序的时间复杂度通常为 O(n \log n)。
空间复杂度:快速排序是一种 原地排序算法,即不需要额外的存储空间,除了递归调用栈所占的空间。空间复杂度是 O(log n),这是因为递归调用的栈深度最多为 \log n(当每次都能将数组平分时)。
不稳定
操作时需要与枢轴量比较大小 根据大小丢到前面或者后面 肯定不稳定