时间复杂度
-
时间复杂度是用来估计算法运行时间的一个式子(单位)——执行的次数
-
一般来说,时间复杂度高的算法比低的算法快
-
如何一眼判断时间复杂度
1、循环减半的过程(O(logn))
2、几次循环就是n的几次方的复杂度(O(nn))
空间复杂度
程序执行所耗内存空间的大小
1、冒泡排序
拿第一个数与后一个数比,如果大,就挪到后面;如果后面的数大,就开始用这个大的数再往后走,直到比到最后一个。接着从头拿新的第一个数去重复,直到排完序
def bubble_sort(li):
for i in range(len(li)):
for j in range(len(li)-i-1): # 每次内层循环比外层少一次
if li[j] > li[j+1]: # <就是倒序
li[j], li[j+1] = li[j+1], li[j]
# 时间复杂度:O(n^2)
# 空间复杂度:O(1)
# 优化
def bubble_sort(li):
for i in range(len(li)):
flag=True
for j in range(len(li)-i-1): # 每次内层循环比外层少一次
if li[j] > li[j+1]:
li[j], li[j+1] = li[j+1], li[j]
flag = False
if flag:
return None
2、选择排序
拿出第一个元素,认为它是最小的,然后与后面的所有元素依次进行比较,如果基准元素比后面的小就不变,否则就交换位置
def select_sort(l):
for i in range(len(l)):
min_ele = i # 把第一个元素的索引定位最小的基准
for j in range(i+1, len(l)):
if l[min_ele] > l[j]:
l[min_ele], l[j] = l[j], l[min_ele]
# 时间复杂度:O(n^2)
# 空间复杂度:O(1)
3、插入排序思路
- 列表被分为有序区和无序区两部分。最初有序区只有一个元素。
- 每次从无序区选择一个元素,插入到有序区的位置,直到无序区变空
def insert_sort(li):
for i in range(1, len(li)): # 初始从索引1开始,索引0是有序区
tmp = li[i] # 第一个元素
j = i - 1 # 最后一个元素的索引
while j>=0 and tmp<li[j]: # tmp小,就把大的往后排
li[j+1]=li[j]
j=j-1
li[j+1]=tmp
# 时间复杂度:O(n^2)
# 空间复杂度:O(1)
4、快速排序
取一个元素p(第一个元素),使元素p归位,意思就是列表被p分为两部分,左边都比p小,右边的都比p大。然后利用递归完成其他部分的排序
- 排序前:5 7 4 6 3 1 2 9 8
- p归位: 2 1 4 3 5 6 7 9 8
- 目标: 1 2 3 4 5 6 7 8 9
def partition(li, left, right):
tmp = li[left]
while left < right:
while left < right and li[right] >= tmp:
right = right - 1
li[left] = li[right]
while left < right and li[left] <= tmp:
left = left + 1
li[right] = li[left]
li[left] = tmp
return left
def Quick_Sort(li, left, right):
if left < right:
mid = partition(li, left, right) ### O(n)
Quick_Sort(li, left, mid-1) #### O(logn)
Quick_Sort(li, mid+1,right)
# 时间复杂度:O(nlogn)
另一种实现
def quick_sort(b):
"""快速排序"""
if len(b) < 2:
return b
# 选取基准,随便选哪个都可以,选中间的便于理解
mid = b[len(b) // 2]
# 定义基准值左右两个数列
left, right = [], []
# 从原始数组中移除基准值
b.remove(mid)
for item in b:
# 大于基准值放右边
if item >= mid:
right.append(item)
else:
# 小于基准值放左边
left.append(item)
# 使用迭代进行比较
return quick_sort(left) + [mid] + quick_sort(right)
print(quick_sort(ls))
5、归并排序
分解:先将列表劈开为两部分,然后再将两个子列表分别劈开,直到每个都是单个元素,单个元素一定是有序的
合并:然后递归的合并相邻的拆分项
### 时间复杂度:O(nlogn)
### 空间复杂度:O(n)
#### python 底层 sorted()函数, 采用的排序算法是 TimSorted 包含了归并排序和插入排序
#### TimSorted 的时间复杂度是:O(nlogn)
def merge(li, low, mid, high):
i = low
j = mid + 1
ltmp = []
while i <= mid and j <=high:
if li[i] <= li[j]
ltmp.append(li[i])
i += 1
else:
ltmp.append(li[j])
j += 1
while i <= mid:
ltmp.append(li[i])
i += 1
while j <= high:
ltmp.append(li[j])
j += 1
li[low:high+1] = ltmp # 将临时列表赋值给原列表
def merge_sort(li, low, high):
if low < high:
mid = (low + high) // 2
# 分解
merge_sort(li, low, mid)
merge_sort(li, mid+1, high)
# 合并
merge(li, low, mid, high)
6、计数排序
计数排序的原理如下:
1、假如有一个长度为5的列表a:[1, 2, 3, 4, 5]
2、我们先生成一个长度为a列表最大元素+1的临时列表b,其元素都为0:[0, 0, 0, 0, 0, 0]
3、遍历原列表a,将列表a的元素当作列表b的索引,然后:b[a元素] += 1
4、这样得到的b列表就会变成:[0,1,1,1,1,1]
5、将b列表的索引赋值给a列表就完成了排序
def count_sort(li):
count = [0 for x in range(max(li)+1)]
for i in li:
count[i] += 1
li.clear()
for index, num in enumerate(count):
# index:索引 num:索引出现的次数
for x in range(num): # 防止某些数出现两次及以上
li.append(index)