时间复杂度,空间复杂度
- 时间复杂度:时间复杂度描述了算法的执行时间与输入规模之间的关系。它表示算法解决问题所需的时间量级。常见的时间复杂度包括O(1)(常数时间)、O(log n)(对数时间)、O(n)(线性时间)、O(n^2)(平方时间)等。时间复杂度越低,算法执行的速度越快。
- 空间复杂度:空间复杂度描述了算法在运行过程中所需的额外空间与输入规模之间的关系。它表示算法执行所需的额外空间的量级。常见的空间复杂度包括O(1)(常数空间)、O(n)(线性空间)、O(n^2)(平方空间)等。空间复杂度越低,算法所需的额外空间越少。
冒泡排序:
解释:通过相邻元素比较,把最大的移动到最后
当我们把一个列表 [5, 3, 8, 2, 1]
带入冒泡排序算法中,可以看到每一轮循环都会将最大的元素移到列表的末尾。
初始列表: [5, 3, 8, 2, 1]
第一轮循环:
- 比较
5
和3
,交换位置,列表变为[3, 5, 8, 2, 1]
- 比较
5
和8
,不需要交换 - 比较
8
和2
,交换位置,列表变为[3, 5, 2, 8, 1]
- 比较
8
和1
,交换位置,列表变为[3, 5, 2, 1, 8]
第二轮循环: - 比较
3
和5
,不需要交换 - 比较
5
和2
,交换位置,列表变为[3, 2, 5, 1, 8]
- 比较
5
和1
,交换位置,列表变为[3, 2, 1, 5, 8]
- 比较
5
和8
,不需要交换
第三轮循环: - 比较
3
和2
,交换位置,列表变为[2, 3, 1, 5, 8]
- 比较
3
和1
,交换位置,列表变为[2, 1, 3, 5, 8]
- 比较
3
和5
,不需要交换 - 比较
5
和8
,不需要交换
第四轮循环: - 比较
2
和1
,交换位置,列表变为[1, 2, 3, 5, 8]
- 比较
2
和3
,不需要交换 - 比较
3
和5
,不需要交换 - 比较
5
和8
,不需要交换
最终排序结果为[1, 2, 3, 5, 8]
。可以看到,通过每一轮循环,最大的元素都会慢慢地“冒泡”到列表的末尾,直到整个列表排序完成。
代码实现:
def bubble_sort(arr):
n = len(arr)
for i in range(n):
for j in range(n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
# 测试示例
arr = [5, 3, 8, 2, 1]
sorted_arr = bubble_sort(arr)
print(sorted_arr)
时间空间复杂度:
时间复杂度:当输入列表已经是有序的,冒泡排序只需要进行一轮比较,时间复杂度为O(n)。其他情况下是O(n²)
空间复杂度:O(1),已经是常数空间复杂度最低了。
插入排序
解释:从未排序的部分逐个取出元素,将其插入到已排序的部分的正确位置。
当我们将一个列表 [5, 3, 8, 2, 1]
带入插入排序算法中,可以看到每一步都将一个元素插入到已排序部分的正确位置。
初始列表: [5, 3, 8, 2, 1]
第一步:将 3
插入到已排序部分 [5]
的正确位置,列表变为 [3, 5, 8, 2, 1]
第二步:将 8
插入到已排序部分 [3, 5]
的正确位置,列表变为 [3, 5, 8, 2, 1]
第三步:将 2
插入到已排序部分 [3, 5, 8]
的正确位置,列表变为 [2, 3, 5, 8, 1]
第四步:将 1
插入到已排序部分 [2, 3, 5, 8]
的正确位置,列表变为 [1, 2, 3, 5, 8]
代码实现:
def insertion_sort(arr):
n = len(arr)
for i in range(1, n):
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
arr[j+1] = arr[j]
j -= 1
arr[j+1] = key
return arr
# 测试示例
arr = [5, 3, 8, 2, 1]
sorted_arr = insertion_sort(arr)
print(sorted_arr)
时间空间复杂度:
时间复杂度:当是有序的情况下,每个元素只需要和它前面的元素比较一次即可确定位置,时间复杂度是O(n)
其他情况下为O(n²)
空间复杂度:O(1),已经是常数空间负责度最低了
快速排序
解释:要求时间复杂度小时使用,通过选择一个基准元素,将数组划分为两个子数组,使得左侧子数组的所有元素小于等于基准元素,右侧子数组的所有元素大于基准元素,然后递归地对子数组进行排序。
假设有一个待排序的列表:[6, 1, 4, 9, 2, 8]
。
- 选择基准元素:
选择一个基准元素(pivot),可以是列表中的任意一个元素。假设选择第一个元素作为基准,即pivot = 6
。 - 分割操作:
将列表中的元素分为两个部分:小于等于基准的元素和大于基准的元素。
列表:[6, 1, 4, 9, 2, 8]
将列表分割为:[1, 4, 2]
和[9, 8]
。 - 递归排序:
对分割后的两个部分进行递归排序。
对左侧部分[1, 4, 2]
进行排序:- 选择基准元素,假设选择第一个元素作为基准,即
pivot = 1
。 - 分割操作,将列表分割为:
[]
和[4, 2]
。 - 递归排序,对右侧部分
[4, 2]
进行排序。- 选择基准元素,假设选择第一个元素作为基准,即
pivot = 4
。 - 分割操作,将列表分割为:
[2]
和[]
。 - 递归排序,对左侧部分
[2]
进行排序。此时,该部分已经有序。
- 选择基准元素,假设选择第一个元素作为基准,即
- 合并操作,将排序好的左侧部分、基准元素和排序好的右侧部分合并。得到
[1, 2, 4]
。
对右侧部分[9, 8]
进行排序: - 选择基准元素,假设选择第一个元素作为基准,即
pivot = 9
。 - 分割操作,将列表分割为:
[8]
和[]
。 - 递归排序,对左侧部分
[8]
进行排序。此时,该部分已经有序。
- 选择基准元素,假设选择第一个元素作为基准,即
- 合并操作:
将排序好的左侧部分、基准元素和排序好的右侧部分合并。得到最终的有序列表。
最终排序结果为[1, 2, 4, 6, 8, 9]
。
代码实现:
def quick_sort(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 quick_sort(left) + [pivot] + quick_sort(right)
# 测试示例
arr = [6, 1, 4, 9, 2, 8]
sorted_arr = quick_sort(arr)
print(sorted_arr)
时间空间复杂度:
时间复杂度:平均时间复杂度为O(nlogn),当输入列表已经有序或基本有序时,快速排序的时间复杂度会退化到O(n^2)。例如,如果每次划分都选择列表中的最大或最小元素作为基准,那么划分的结果会非常不均匀。
空间复杂度:O(logn)