Python 基础排序算法
冒泡排序(bubble sort)
思路
以升序为例:
从第一个数开始向后两两对比,将大的数一直向后移动,直至最大的数移到最后,再找第二大的数
最好情况:O(n)
一般情况:O(n^2)
最坏情况:O(n^2)
代码
import random
def bubble_sort(l):
for i in range(len(l) - 1):
exchange = False
for j in range(len(l) - 1 - i):
if l[j] > l[j + 1]:
l[j], l[j + 1] = l[j + 1], l[j]
exchange = True
if not exchange: # 如果一次循环中都没有交换,则说明已经排序完成,可以提前结束
break
if __name__ == '__main__':
data = list(range(1000))
random.shuffle(data)
bubble_sort(data)
print(data)
选择排序(selection sort)
思路
以升序为例:
从第一个位置开始向后寻找,找到最小的数,放在这个位置,之后向后移一个位置
时间复杂度
最好情况:O(n^2)
一般情况:O(n^2)
最坏情况:O(n^2)
代码
import random
def selection_sort(l):
for i in range(len(l) - 1):
min_num = i
for j in range(i + 1, len(l)):
if l[j] < l[min_num]:
min_num = j
if min_num != i:
l[i], l[min_num] = l[min_num], l[i]
if __name__ == '__main__':
data = list(range(1000))
random.shuffle(data)
selection_sort(data)
print(data)
插入排序(insertion sort)
思路
以升序为例:
将第一个数分为有序队列,后面为无序队列。从二个数开始,依次插入前面有序队列
时间复杂度
最好情况:O(n)
一般情况:O(n^2)
最坏情况:O(n^2)
代码
import random
def insertion_sort(l):
for i in range(1, len(l)):
tem = l[i]
j = i - 1
while j >= 0 and tem < l[j]:
l[j + 1] = l[j]
j -= 1
l[j + 1] = tem
if __name__ == '__main__':
data = list(range(1000))
random.shuffle(data)
insertion_sort(data)
print(data)
快速排序(quicksort)
Python 的 sort 时间复杂度为 O(nlogn),但是因为是 C 写的,所以会快
思路
以升序为例:
从第一个数开始,将其他数分成两部分,小于这个数的放在左边,大于这个数的放在右边,分出的左右两边分别继续执行。
时间复杂度
最好情况:O(nlogn)
一般情况:O(nlogn)
最坏情况:O(n^2)
代码
import random
def quick_sort(l, left, right):
if left < right:
mid = partition(l, left, right)
quick_sort(l, left, mid - 1)
quick_sort(l, mid + 1, right)
def partition(l, left, right):
tem = l[left]
while left < right:
# 一次循环左右各移动一位
while left < right and l[right] > tem:
# 当前位不需要换位置,指针向内移动一位,直至遇到需要换位置的位
right -= 1
l[left] = l[right] # 将右边需要换位置的换到左边(取出的数之前的位置)
while left < right and l[left] < tem:
left += 1
l[right] = l[left]
l[left] = tem
return left
if __name__ == '__main__':
data = list(range(1000))
random.shuffle(data)
quick_sort(data, 0, len(data) - 1)
print(data)
堆排序(heapsort)
思路
以升序为例:
借助最大堆(大根堆)的特性,依次取出堆中最大的数(根结点的数),再对堆进行调整
时间复杂度
最好情况:O(nlogn)
一般情况:O(nlogn)
最坏情况:O(nlogn)
代码
import random
def sift(l, low, high):
i = low # 指向父节点
j = 2 * i + 1 # 指向左节点
tem = l[i] # 获取父节点的值
while j <= high: # 如果子节点在堆中(有子节点)
if j < high and l[j] < l[j + 1]: # 有右节点且比左节点大
j += 1 # 指向右节点
if tem < l[j]: # 如果根结点小于子节点中较大的那个
l[i] = l[j] # 将较大的值放入父节点
i = j # 指向下一层树
j = 2 * i + 1
else:
break
l[i] = tem # 将循环开始时的父节点数据写入
def heap_sort(l):
n = len(l)
for i in range(n // 2 - 1, -1, -1):
sift(l, i, n - 1)
# 堆建好了
for i in range(n - 1, -1, -1): # i 指向堆堆最后
l[0], l[i] = l[i], l[0] # 将当前最大数移到最后,最后的数移到根结点
sift(l, 0, i - 1)
if __name__ == '__main__':
data = list(range(1000))
random.shuffle(data)
heap_sort(data)
print(data)
归并排序(merge sort)
思路
以升序为例:
由于将两个有序列表合并比较简单,所以将列表无限分割成仅有一个元素的有序列表,之后再合并
时间复杂度
最好情况:O(nlogn)
一般情况:O(nlogn)
最坏情况:O(nlogn)
代码
import random
def merge_sort(l, low, high):
if low < high:
mid = (low + high) // 2
merge_sort(l, low, mid) # 前半部分迭代
merge_sort(l, mid + 1, high) # 后半部分迭代
merge(l, low, mid, high) # 调整
def merge(l, low, mid, high):
"""
用于将两个有序列表进行合并
"""
i = low # 指向左边有序列表第一个元素
j = mid + 1 # 指向右边有序列表第一个元素
tem_l = [] # 临时列表,用于存放合并后列表
while i <= mid and j <= high:
# 当左右列表中都有元素时
if l[i] <= l[j]:
tem_l.append(l[i])
i += 1
else:
tem_l.append(l[j])
j += 1
while i <= mid:
# 当左边列表中有元素时,直接将剩下的元素全部插入临时列表
tem_l.append(l[i])
i += 1
while j <= high:
# 当右边列表中有元素时,直接将剩下的元素全部插入临时列表
tem_l.append(l[j])
j += 1
# 用排序完成的元素替代未排序的元素
l[low:high + 1] = tem_l
if __name__ == '__main__':
data = list(range(1000))
random.shuffle(data)
merge_sort(data, 0, len(data) - 1)
print(data)
希尔排序(Shell's sort)
思路
以升序为例:
将元素分为n组,每一组进行插入排序,再将每组元素增加直至所有元素都在一组中
时间复杂度
O((1 + τ)n)
一般情况:O(1.3n)
代码
import random
def shell_sort(l):
gap = len(l) // 2
while gap > 0:
for i in range(gap, len(l)):
tem = l[i]
j = i - gap # 指向同组前一个元素
while j >= 0 and tem < l[j]: # 存在前一个元素并且比当前元素大
l[j + gap] = l[j] # 将前一个元素移到后面一位
j -= gap # 指向前一个元素
l[j + gap] = tem
gap //= 2
if __name__ == '__main__':
data = list(range(1000))
random.shuffle(data)
shell_sort(data)
print(data)