排序算法时空复杂度一览
- 稳定:如果 a 原本在 b 前面,而 a=b,排序之后 a 仍然在 b 的前面。
- 不稳定:如果 a 原本在 b 的前面,而 a=b,排序之后 a 可能会出现在 b 的后面。
插入类排序
插入排序
- 最快:正序
- 最慢:反序
def insertion_sort(lst):
for i in range(1, len(lst)):
pre_index = i - 1
cur = lst[i]
while pre_index >= 0 and lst[pre_index] > cur:
lst[pre_index + 1] = lst[pre_index]
pre_index -= 1
lst[pre_index + 1] = cur
return lst
希尔排序
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
def shell_sort(lst):
step = len(lst) // 2
while step != 0:
for i in range(step, len(lst)):
while i >= step and lst[i] < lst[i - step]:
lst[i - step], lst[i] = lst[i], lst[i - step]
i = i - step
step = step // 2
return lst
选择类排序
选择排序
任何情况O(N^2),数据规模越小越好,不占用额外内存
def selection_sort(lst):
for i in range(len(lst) - 1):
min_index = i
for j in range(i + 1, len(lst)):
if lst[j] < lst[min_index]:
min_index = j
if i != min_index:
lst[i], lst[min_index] = lst[min_index], lst[i]
return lst
堆排序
- 大顶堆(大根堆):每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
- 小顶堆(小根堆):每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
def build_max_heap(heap):
heap_size = len(heap)
for i in range(heap_size // 2 - 1, -1, -1):
max_heapify(heap, heap_size, i)
def max_heapify(heap, heap_size, root):
left = 2 * root + 1
right = left + 1
largest = root
if left < heap_size and heap[largest] < heap[left]:
largest = left
if right < heap_size and heap[largest] < heap[right]:
largest = right
if largest != root:
heap[largest], heap[root] = heap[root], heap[largest]
max_heapify(heap, heap_size, largest)
def heap_sort(heap):
build_max_heap(heap)
for i in range(len(heap) - 1, 0, -1):
heap[0], heap[i] = heap[i], heap[0]
max_heapify(heap, i, 0)
return heap
# 利用heapq标准模块,优先队列
def heap_sort(lst):
h = []
for value in lst:
heapq.heappush(h, value)
return [heapq.heappop(h) for _ in range(len(h))]
交换类排序
冒泡排序
- 最快:正序
- 最慢:反序
def bubble_sort(lst):
for i in range(len(lst) - 1):
for j in range(len(lst) - 1 - i):
if lst[j] > lst[j + 1]:
lst[j], lst[j + 1] = lst[j + 1], lst[j]
return lst
快速排序
- 最快:每次均匀划分
- 最慢:有序(正序或者逆序)
def quick_sort(lst):
if len(lst) <= 1:
return lst
else:
pivot = lst[0]
left = [i for i in lst[1:] if i <= pivot]
right = [i for i in lst[1:] if i > pivot]
return quick_sort(left) + [pivot] + quick_sort(right)
归并类排序
归并排序
始终都是 O(nlogn) 的时间复杂度。代价是需要额外的内存空间
def merge_sort(lst):
if len(lst) <= 1:
return lst
mid = len(lst) // 2
left = merge_sort(lst[:mid])
right = merge_sort(lst[mid:])
return merge(left, right)
def merge(left, right):
i, j = 0, 0
res = []
while i < len(left) and j < len(right):
if left[i] <= right[j]:
res.append(left[i])
i += 1
else:
res.append(right[j])
j += 1
res.extend(left[i:])
res.extend(right[j:])
return res
分布类排序
计数排序
将输入的数据值转化为键存储在额外开辟的数组空间中
def counting_sort(lst):
max_value = max(lst)
count = [0] * (max_value + 1)
for num in lst:
count[num] += 1
res = []
for i in range(len(count)):
for _ in range(count[i]):
res.append(i)
return res
桶排序
划分多个范围相同的区间,每个子区间自排序,最后合并。
计数排序升级版,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过映射函数,将待排序数组中的元素映射到各个对应的桶中,对每个桶中的元素进行排序,最后将非空桶中的元素逐个放入原序列中。
最快:当输入的数据可以均匀的分配到每一个桶中。 最慢:当输入的数据被分配到了同一个桶中。
def bucket_sort(lst):
bucket_num = (max(lst) - min(lst)) // len(lst) + 1
buckets = [[] for _ in range(bucket_num)]
for num in lst:
buckets[(num - min(lst)) // len(lst)].append(num)
res = []
for bucket in buckets:
bucket.sort()
res.extend(bucket)
return res
基数排序
基数排序是一种非比较型整数排序算法。
其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
def radix_sort(lst):
max_value = max(lst)
for i in range(len(str(max_value))):
bucket_list = [[] for _ in range(10)]
for num in lst:
bucket_list[num // (10 ** i) % 10].append(num)
lst.clear()
for bucket in bucket_list:
for num in bucket:
lst.append(num)
return lst