排序方法 |
平均情况 |
最好情况 |
最坏情况 |
辅助空间 |
稳定性 |
冒泡排序 |
O(n^2) |
O(n) |
O(n^2) |
O(1) |
稳定 |
选择排序 |
O(n^2) |
O(n^2) |
O(n^2) |
O(1) |
稳定 |
插入排序 |
O(n^2) |
O(n) |
O(n^2) |
O(1) |
稳定 |
希尔排序 |
O(n^2) |
O(n^1.3) |
O(n^2) |
O(1) |
不稳定 |
堆排序 |
O(nlogn) |
O(nlogn) |
O(nlogn) |
O(1) |
不稳定 |
归并排序 |
O(nlogn) |
O(nlogn) |
O(nlogn) |
O(n) |
稳定 |
快速排序 |
O(nlogn) |
O(nlogn) |
O(n^2) |
O(logn) ~ O(n) |
不稳定 |
1. 冒泡排序
# encoding:utf8
def BubbleSort(arr):
n = len(arr)
for i in range(n):
for j in range(n - 1, i, -1):
if arr[j - 1] > arr[j]:
arr[j], arr[j - 1] = arr[j - 1], arr[j]
return arr
# 冒泡排序优化:用Flag避免数组已经有序的情况
def BubbleSort2(arr):
n = len(arr)
Flag = True
for i in range(n):
if not Flag:
break
Flag = False
for j in range(n - 1, i, -1):
if arr[j - 1] > arr[j]:
arr[j], arr[j - 1] = arr[j - 1], arr[j]
Flag = True
return arr
# 最优时间复杂度 O(n^2), 最差时间复杂度 O(n)
if __name__ == "__main__":
arr = [6, 3, 2, 5, 1, 6, 3]
print(BubbleSort(arr))
2.1 快速排序
# encoding:utf8
def partition(arr, low, high):
# 用数组中的第一个记录作为枢纽记录
privotKey = arr[low]
# 从数组的两端交替向中间扫描
while low < high:
while low < high and arr[high] >= privotKey:
high -= 1
# 将比枢纽小的数交换到低端
arr[low], arr[high] = arr[high], arr[low]
while low < high and arr[low] <= privotKey:
low += 1
# 将比枢纽大的数交换到高端
arr[low], arr[high] = arr[high], arr[low]
# 返回枢纽所在位置
return low
def QuickSort(arr, low, high):
if low < high:
# 算出枢纽值,将数组一分为二
privot = partition(arr, low, high)
# 对低子表递归排序
QuickSort(arr, low, privot - 1)
# 对高子表递归排序
QuickSort(arr, privot + 1, high)
# 最优时间复杂度 O(nlogn), 最差时间复杂度 O(n^2)
# 最优空间复杂度 O(logn), 最差空间复杂度 O(n)
if __name__ == "__main__":
arr = [10, 7, 8, 9, 1, 5, 2, 4, 6, 24]
QuickSort(arr, 0, len(arr) - 1)
print(arr)
2.2 快速排序优化1
# encoding:utf8
# 优化选取枢纽,选取的枢纽值决定交换次数,我们尽量将枢纽值取到数组的中间位置
# 选择三数取中,即取三个关键字先进行排序,将中间数作为枢纽。
def partition(arr, low, high):
mid = low + (high - low) // 2
# 交换左端与右端,保证左端较小
if arr[low] > arr[high]:
arr[low], arr[high] = arr[high], arr[low]
# 交换中间与右端数据,保证中间较小
if arr[mid] > arr[high]:
arr[mid], arr[high] = arr[high], arr[mid]
# 交换中间与左端数据,保证左端较小
if arr[mid] > arr[low]:
arr[mid], arr[low] = arr[low], arr[mid]
# 此时low是三个数中的中间值
pivotKey = arr[low]
while low < high:
if low < high and arr[high] >= pivotKey:
high -= 1
arr[low], arr[high] = arr[high], arr[low]
if low < high and arr[low] <= pivotKey:
low += 1
arr[low], arr[high] = arr[high], arr[low]
return low
def QuickSort(arr, low, high):
if low < high:
pivot = partition(arr, low, high)
QuickSort(arr, low, pivot - 1)
QuickSort(arr, pivot + 1, high)
arr = [10, 7, 8, 9, 1, 5, 2, 4, 6, 24]
QuickSort(arr, 0, len(arr) - 1)
print(arr)
2.2 快速排序优化2
# encoding:utf8
# 优化不必要的交换
def partition(arr, low, high):
# 三数交换
mid = low + (high - low) // 2
if arr[low] > arr[high]:
arr[low], arr[high] = arr[high], arr[low]
if arr[mid] > arr[high]:
arr[mid], arr[high] = arr[high], arr[mid]
if arr[mid] > arr[low]:
arr[mid], arr[low] = arr[low], arr[mid]
pivotKey = arr[low]
# 设置一个哨兵元素
pre = pivotKey
while low < high:
while low < high and arr[high] >= pivotKey:
high -= 1
# 采用替换而不是交换
arr[low] = arr[high]
while low < high and arr[low] <= pivotKey:
low += 1
arr[high] = arr[low]
arr[low] = pre
return low
def QuickSort(arr, low, high):
if low < high:
pivot = partition(arr, low, high)
QuickSort(arr, low, pivot - 1)
QuickSort(arr, pivot + 1, high)
arr = [10, 7, 8, 9, 1, 5, 2, 4, 6, 2]
QuickSort(arr, 0, len(arr) - 1)
print(arr)
2.3 快速排序优化3
# encoding:utf8
# 优化递归
def partition(arr, low, high):
# 三数取中
mid = low + (high - low) // 2
if arr[low] > arr[high]:
arr[low], arr[high] = arr[high], arr[low]
if arr[mid] > arr[high]:
arr[mid], arr[high] = arr[high], arr[mid]
if arr[mid] > arr[low]:
arr[mid], arr[low] = arr[low], arr[mid]
pivotKey = arr[low]
# 设置哨兵元素
pre = pivotKey
while low < high:
while low < high and arr[high] >= pivotKey:
high -= 1
arr[low] = arr[high]
while low < high and arr[low] <= pivotKey:
low += 1
arr[high] = arr[low]
arr[low] = pre
return low
def QuickSort(arr, low, high):
while low < high:
pivot = partition(arr, low, high)
# 对低子表递归排序
QuickSort(arr, low, pivot - 1)
# 尾递归
low = pivot + 1
arr = [10, 7, 8, 9, 1, 5, 2, 4, 6, 13, 4]
QuickSort(arr, 0, len(arr) - 1)
print(arr)
3. 归并排序
# encoding:utf8
def merge(s1, s2, s):
"""将两个列表是s1,s2按顺序融合为一个列表s,s为原列表"""
# j和i就相当于两个指向的位置,i指s1,j指s2
i = j = 0
while i + j < len(s):
# j == len(s2)时说明s2走完了,或者s1没走完并且s1中该位置是最小的
if j == len(s2) or (i < len(s1) and s1[i] < s2[j]):
s[i + j] = s1[i]
i += 1
else:
s[i + j] = s2[j]
j += 1
def MergeSort(arr):
n = len(arr)
# 剩一个或没有直接返回,不用排序
if n <= 1:
return arr
# 拆分
mid = n // 2
left = arr[:mid]
right = arr[mid:]
# 子序列递归调用排序
MergeSort(left)
MergeSort(right)
# 合并
merge(left, right, arr)
# 最优时间复杂度 O(nlogn)
# 最优空间复杂度 O(n + logn)
if __name__ == '__main__':
s = [1, 7, 3, 5, 4, 2, 5, 3]
MergeSort(s)
print(s)
4. 归并排序
# encoding:utf8
def merge(s1, s2, s):
"""将两个列表是s1,s2按顺序融合为一个列表s,s为原列表"""
# j和i就相当于两个指向的位置,i指s1,j指s2
i = j = 0
while i + j < len(s):
# j == len(s2)时说明s2走完了,或者s1没走完并且s1中该位置是最小的
if j == len(s2) or (i < len(s1) and s1[i] < s2[j]):
s[i + j] = s1[i]
i += 1
else:
s[i + j] = s2[j]
j += 1
def MergeSort(arr):
n = len(arr)
# 剩一个或没有直接返回,不用排序
if n <= 1:
return arr
# 拆分
mid = n // 2
left = arr[:mid]
right = arr[mid:]
# 子序列递归调用排序
MergeSort(left)
MergeSort(right)
# 合并
merge(left, right, arr)
# 最优时间复杂度 O(nlogn)
# 最优空间复杂度 O(n + logn)
if __name__ == '__main__':
s = [1, 7, 3, 5, 4, 2, 5, 3]
MergeSort(s)
print(s)
5. 简单选择排序
# encoding:utf8
def SelectSort(arr):
# 记录每一个元素的下标
for i in range(len(arr)):
min = i
for j in range(i + 1, len(arr)):
if arr[min] > arr[j]:
min = j
if i != min:
arr[i], arr[min] = arr[min], arr[i]
return arr
# 最优时间复杂度 O(1), 最差时间复杂度 O(n^2)
if __name__ == '__main__':
s = [1, 7, 3, 5, 4, 2, 5, 5]
res = SelectSort(s)
print(res)
6. 堆排序
# encoding:utf8
from collections import deque
"""
堆排序思想:
首先将待排序的数组构造出一个大根堆
取出这个大根堆的堆顶节点(最大值),与堆的最下最右的元素进行交换,然后把剩下的元素再构造出一个大根堆
重复第二步,直到这个大根堆的长度为1,此时完成排序。
"""
# 交换两个元素
def swap(l, i, j):
l[i], l[j] = l[j], l[i]
return l
def swap_param(L, i, j):
L[i], L[j] = L[j], L[i]
return L
# 调整堆
def heap_adjust(arr, start, end):
temp = arr[start]
i = start
# 沿关键字较大的孩子结点(2i)向下筛选
j = 2 * i
# 遍历当前节点的孩子结点
while j <= end:
# 如果当前节点不是最后一个节点而且左孩子小于右孩子,记录较大值
if j < end and arr[j] < arr[j + 1]:
j += 1 # j 为关键字中较大的记录的下标
if temp >= arr[j]:
break
else:
arr[i] = arr[j]
i = j
j = 2 * i
arr[i] = temp # 交换两个数字
def heap_sort(arr):
"""
由于堆排序是一种完全二叉树,数组下标是从0开始的,二叉树的节点从1开始,
所以这里我们引入一个辅助空间,用deque追加一个辅助位
"""
l = deque(arr)
l.appendleft(0)
# 引入了一个辅助空间,这里l_len的长度-1
l_len = len(l) - 1
# first_sort_count 是有孩子的节点
first_sort_count = l_len // 2
# 从后往前将序列调整为一个大根堆
print(l)
for i in range(first_sort_count, 0, -1):
heap_adjust(l, i, l_len)
print(l)
# 从后往前将堆顶元素和堆末尾元素进行交换, 然后把剩下的元素调整为一个大根堆
for i in range(l_len, 0, -1):
# 将堆顶记录和当前未经排序的子序列的最后一个记录交换
l = swap_param(l, 1, i)
# 将[1 ~ i-1]重新调整为大顶堆
heap_adjust(l, 1, i - 1)
# print(l)
return [l[i] for i in range(1, len(l))]
# 时间复杂度 O(nlogn)
if __name__ == '__main__':
s = [1, 7, 3, 5, 4, 2, 5, 9, 4, 3, 5]
res = heap_sort(s)
print(res)
7. 直接插入排序
# encoding:utf8
def insert_sort(arr):
for i in range(1, len(arr)):
# 保存当前元素的值,依次比较选择
key = arr[i]
# 从当前记录开始往前进行比较
j = i - 1
while j >= 0 and key < arr[j]:
arr[j + 1] = arr[j] # 记录后移
j -= 1
arr[j + 1] = key # 插入到正确的位置
return arr
# 时间复杂度 O(n^2)
if __name__ == '__main__':
arr = [1, 7, 3, 5, 4, 2, 5, 9, 4, 3, 5]
res = insert_sort(arr)
print(res)