本文排序算法分析和代码参考:https://www.bilibili.com/video/BV1Aq4y1S7nb?p=11&spm_id_from=pageDriver
0. 排序算法的分类
- 比较类算法:通过比较来确定元素间的先后顺序,时间复杂度不能突破O(nlogn),非线性时间比较类排序
- 非比较类的时间复杂度:使用空间换取时间
- 算法稳定性, a=b,且a在b前面,排序之后a是否仍然在b的前面
- 时间复杂度
- 空间复杂度
1. 冒泡排序
1.1 算法思路
- 外层循环每一次都对相邻的元素进行排序,将小的元素放到上面(或者),内层循环遍历到倒数第二个;
- 重复进行排序;
1.2 Python实现
python代码:
def bubbleSort(nums):
# 外层遍历,记录遍历次数
for i in range(len(nums)):
# 内层遍历,每层遍历len(nums)-1-i次即可
for j in range(len(nums) - 1 - i):
# 如果下一个元素等于当前元素,交换两个位置
if nums[j] > nums[j + 1]:
nums[j], nums[j + 1] = nums[j + 1], nums[j]
return nums
nums = [2, 3, 8, 4, 2, 0, 7, 8]
print(bubbleSort(nums))
1.3 分析
排序算法的稳定性:排序前后,数值相同的两个元素相对位置不发生变化,稳定的
时间复杂度:O(N2)
2. 快速排序
2.1 算法思路
算法流程:
- 先从数列中选择一个数作为基准
- 将比这个数大的全部放在右边,小的有放在左边
- 对左右区间重复第二步,直到各个区间只有一个数
一次分区过程:
- 设置两个变量,i=0,j=N-1
- 以第一个数为基准数,赋值给pivot,即pivot=A[0]=A[i]
- j从后往前搜索(j–),找到第一个小于pivot的值A[j],将A[j]和A[i]交换,更新基准pivot
- i从前向后搜索(i++),找到第一个大于pivot的值A[i],更新A[j]和A[i]交换
- 重复3,4直到i==j
一次快排之后原本的数组被分成两部分,左边的都小于基准,右边的都大于基准。然后在左
边和右边分别进行排序;
引用自身考虑使用递归算法:
2.2 Python实现
def quickSort(nums, low, hight):
# 设置头尾指针,i和j
i, j = low, hight
while i < j:
# 设置当前基准为nums[i]
pivot = nums[i]
# 从后往前遍历,如果nums[j]>=pivot,则继续遍历,直到小于基准
while i < j and nums[j] >= pivot:
j -= 1
# 交换此时的i和j对应元素并更新基准为nums[j]
nums[j], nums[i] = nums[i], nums[j]
pivot = nums[j]
# 从前向后遍历,如果nums[i]<=pivot,则继续遍历,直到大于基准
while i < j and nums[i] <= pivot:
i += 1
# 交换此时的i和j对应元素并更新基准为nums[i]
nums[j], nums[i] = nums[i], nums[j]
# 进行递归,对基准值左边,右边元素分别递归;
quickSort(nums, low, j - 1)
quickSort(nums, j + 1, hight)
return nums
nums = [0, 2, -2, -3, 3]
print(quickSort(nums, 0, len(nums) - 1))
2.3 分析
稳定性:不稳定,相同元素排序之后相对位置可能发生变化;
时间复杂度: 递归的,每次排序时间复杂度是O(N)的,排序次数是二分的O(logN),因此,快速排序的总体时间复杂度是O(NlogN),最坏的情况变成O(N2)。
3. 插入排序
3.1 算法思路
把n个待排序的元素看成一个有序表,一个无序表。开始时有序表中只有一个元素,无序表中间有n-1个元素,排序是每次从无序表中间取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表。重复n-1次可以完成排序。
3.2 Python实现
def insertSort(nums):
# 外层循环,遍历nums
for i in range(0, len(nums) - 1):
j = i+1
# 从i开始遍历后面的无序列表,并从后向前插入到有序列表中
while len(nums) > j >= 1:
# 当前元素小于前一个元素则交换两个元素
if nums[j] < nums[j - 1]:
nums[j], nums[j - 1] = nums[j - 1], nums[j]
j -= 1
return nums
nums = [-5, -2, -4, 6, 1, 1, 3]
print(insertSort(nums))
3.3 分析
稳定性:稳定的
时间复杂度:O(N2),外层循环n-1次,每一次排序次数和n相关
4. 希尔排序
4.1 算法思路
简单插入排序的改良版本
简单排序的方法:如果数据最小值在在后面,其排序的时间会变得比较长
按照下标进行分组,对于每组进行直接的插入算法排序,随着增量逐渐减少,每组元素越来越多,当增量为1时,整个元素分成一组,算法终止;
4.2 python实现
def shellSort(nums):
l = len(nums)
# 初始增量设置为len(nums)//2
gap = l // 2
# 外层对增量进行循环
while gap >= 1:
# 第二层从索引为gap开始往后遍历nums
for i in range(gap, len(nums)):
# 第三层从索引为i,往前遍历,每次向前移动gap
for j in range(i, -1, -gap):
# 如果num[j]<nums[j-gap],交换两个元素
if j >= gap and nums[j] < nums[j - gap]:
nums[j], nums[j - gap] = nums[j - gap], nums[j]
# 更新gap
gap = gap // 2
return nums
nums = [1, 2, 4, 1, 1, 2, -1, 0, 2, 5]
print(shellSort(nums))
4.3 分析
稳定性:不稳定,是一种插入排序
时间复杂度:O(nlogn)~O(n2),两者之间
和顺序有关,和递归方式有关
5. 选择排序
5.1 算法分析
首先在没有排序的序列中找到最小元素,放在起始位置。然后,从剩余没有排序的元素中继续寻找最小元素,放到已经排序的序列末尾。直到所有元素排序完成。如图所示:
5.2 Python实现
def selectSort(nums):
# 外层循环遍nums,到len(nums)-1即可
for i in range(len(nums) - 1):
# 内层循环从i+1开始
for j in range(i + 1, len(nums)):
# 设置初始最小值的索引为i
min_index = i
# 如果nums[j]>nums[i],将min_index替换为j
if nums[i] > nums[j]:
min_index = j
# 一轮循环完成之后,将nums[i]和索引值最小对应的值交换
nums[i], nums[min_index] = nums[min_index], nums[i]
return nums
nums = [-1, 2, 1, 0, -1, -2]
print(selectSort(nums))
5.3 分析
稳定性:不稳定
时间复杂度O(N2)
以上是五种排序算法的思路及其python实现代码。这几种算法都是比较类排序算法,后续会介绍堆排序,归并排序,以及计数排序,桶排序(其中后面两个是非比较的排序方法);
see you~