排序算法总结与 Py 实现
这篇文章主要借鉴了地址,原文是 Java 实现的,质量特别棒。
算法分类:
常见排序算法一般分为两大类:
比较型排序:通过比较决定元素的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
非比较排序::不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。
排序算法.png
算法复杂度
算法复杂度.png
相关概念
稳定:即元素的相对顺序是否会发生改变,如 a 原本在 b 前面,而 a=b,排序之后 a 仍然在 b 的前面,则算法是稳定的。有些算法由于判断条件是 >= 或 <= ,这个条件包含了相等的状态,这会导致该元素会落在最后的位置。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。有些算法由于判断条件是 >= 或 <= ,这个条件包含了相等的状态,这会导致该元素会落在最后的位置。
稳定举例:比如你考试成绩一直和班里的另一个同学相同,那么稳定的排序算法就是每次总排名你都在他前头,而不是这次他排你前头,下次你排他前头。
时间复杂度:对排序数据的总的操作次数。
空间复杂度:是指算法在计算机
内执行时所需存储空间的度量,它也是数据规模n的函数。
1、冒泡排序
冒泡排序是一种遍历的排序算法。它重复地走过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。每次遍历都会把一个值放到合适的位置。
1.1 算法描述
比较相邻的元素。如果第一个比第二个大,就交换它们两个;
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
针对所有的元素重复以上的步骤,除了最后一个;
重复步骤1~3,直到排序完成。
1.2 图示
冒泡.png
冒泡.png
1.3 代码
def mao(nums):
n = len(nums)
for i in range(n):
# n个数,遍历 n 次
for j in range(n-i-1):
# 因为每次遍历都会放一个数到合适的位置,所以 - i,
# 又因为是在比较 nums[j] 与 nums[j+1],j+1的操作可以帮住我们上扩大遍历的范围,所以 - 1
if nums[j] > nums[j+1]:
# 注意这里是 > 号,所以不会交换相同的值,所以冒泡是稳定的算法
nums[j], nums[j+1] = nums[j+1], nums[j]
print(nums)
return nums
2、快速排序
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
2.1 算法描述
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
2.2 图示
快速排序.png
代码
def quick(nums, left, right):
if left < right:
i, j = left, right
i 是左指针,j 是右指针
base = nums[i]
#使用待排序列的第一个数做作为基数
print(base)
while i < j:
while i < j and nums[j] >= base:
# j 会 pass 过所有大于等于 base 的值。注意这里也会 pass 过相等的值,这使得快速不是稳定的算法
j -= 1
# 使 j 指针停在小于基数的位数
nums[i] = nums[j]
# 把 nums[j]的值 移到 i 的位置(让小的数把前头的值占了。)
while i < j and nums[i] <= base:
i += 1
# 使 j 指针停在大于基数的位数
nums[j] = nums[i]
# 把 nums[i]的值 移到 j 的位置(让大的数把后头的值占了。)
nums[i] = base
# 在最后 i 会停在合适的位置,(左小右大),其实每次quick都只能保证一个数字排到合适的位置。
print(i)
# 使用递归把剩下的数列排序了
quick(nums, left, i-1)
quick(nums, i+1, right)
return nums