本文参考b站视频: 清华大学博士讲解Python数据结构与算法(完整版)全套100节
地址:https://www.bilibili.com/video/BV1uA411N7c5
冒泡排序
# 冒泡排序,升序
def bubble_sort(li):
flag = False # 一次冒泡没有交换说明有序,直接输出
for i in range(len(li)-1):
for j in range(0,len(li)-1-i): # 有序区放最后
if li[j] > li[j+1]: # 每次将最大的数字放在最后
li[j], li[j+1] = li[j+1], li[j]
flag = True
if not flag:
return li
return li
选择排序
# 选择排序,升序
def select_sort(li):
for i in range(0,len(li)):
max_index = 0 # 每次都需要单独设为无序区的序号,不然如果交换后的序号成为了最大,就会在下一次跳出无序区
for j in range(0,len(li)-i): # 有序区在最后
if li[j] > li[max_index]: # 无序区选择最大
max_index = j
li[j],li[max_index] = li[max_index],li[j]
return li
插入排序
# 插入排序,升序
def insert_sort(li):
for i in range(0,len(li)-1): # 有序部分放在最前面
j = i
while li[j+1] < li[j] and j >= 0:
li[j],li[j+1] = li[j+1],li[j]
j -= 1
return li
快速排序
# 快速排序辅助函数,排一次(相当于二分一次)数据交换情况
def partition(li,left,right):
tmp = li[left]
start = left
while left < right:
while tmp <= li[right] and left < right:
right -= 1
while tmp >= li[left] and left < right:
left += 1
li[left],li[right] = li[right],li[left] # 直接交换左边大于tmp和右边小于tmp的数据,因为反正最后都只剩一个空位
li[left],li[start] = li[start],li[left]
return left # right也可以,需要记录下分割的下标
# 快速排序,升序,递归
def quick_sort(li,left,right):
if left < right:
mid = partition(li,left,right)
quick_sort(li,left,mid-1) # 用li作为输入的列表,是因为li是全局变量,所以每次修改li都会改变
quick_sort(li,mid+1,right) # 每次迭代用left和right表示下一次分割的下标位置
return li
堆排序
# 调整堆顶元素,其他部分都是最大堆,需要输入调整的第一个下标和最后一个下标
# 堆顶元素不一定是最大元素,堆其他部分满足最大堆
def sift(li,first,last):
left = 2 * first + 1
right = 2 * first + 2
while right <= last:
if li[first] >= li[right] and li[first] >= li[left]: # 父节点最大,则直接退出
break
else:
if li[left] > li[right]: # 左节点最大
li[first],li[left] = li[left],li[first]
first = left
else: # 右节点最大
li[first], li[right] = li[right], li[first]
first = right
left = 2 * first + 1 # 下一层
right = 2 * first + 2
# 堆排序,升序,最大堆
def heap_sort(li):
# 建立堆,从下到上(最底层到顶层)
for i in range((len(li)-2)//2,-1,-1):
sift(li,i,len(li)-1)
# 排序,将最后一个元素替换到顶元素,顶元素已经排好序了
for i in range(len(li)-1,0,-1):
li[0],li[i] = li[i],li[0]
sift(li,0,i-1)
return li
归并排序
# 一个列表,前部分顺序排好,后部分顺序排好,将两部分归并
def merge(li,first,mid,last):
tmp_i = first
tmp_j = last
tmp_list = [] # first,mid,last都是下标
first2 = mid+1
while first <= mid and first2 <= last:
if li[first] < li[first2]:
tmp_list.append(li[first])
first += 1
else:
tmp_list.append(li[first2])
first2 += 1
if first <= mid: # 判断+循环,可以直接用while
for i in range(first,mid+1):
tmp_list.append(li[first])
first += 1
elif first2 <= last:
for i in range(first2,last+1):
tmp_list.append(li[first2])
first2 += 1
li[tmp_i:tmp_j+1] = tmp_list # 将合并得到的列表送入li中,不然最后的结果没有变化了
return tmp_list
# 归并排序,升序
def merge_sort(li,first,last): # 因为需要递归(像树一样),所以写入另外两个参数
if first < last: # 用递归都没有写过循环
# 分割
mid = (first + last) // 2
merge_sort(li,first,mid)
merge_sort(li,mid+1,last)
# 排序
merge(li,first,mid,last)
return li
计数排序
# 计数排序,升序,从0开始的整数,需要先算出最大值,无需比较
def count_sort(li):
max_num = max(li)
count_list = [0 for i in range(max_num+1)] # 需要额外开一个空间
for i in li:
count_list[i] += 1
li = []
for index,val in enumerate(count_list): # index为排序数的大小,val为数量
for i in range(val):
li.append(index)
return li
希尔排序
# 希尔排序,升序
# 为什么最开始分组少,最后分组多啊?
# 分组多是为了一次前进一大步,相比于插入排序
def shell_sort(li):
gap = len(li)//2
while gap >= 1: # 为啥需要增量为1的排序啊,那不就是冒泡了吗?这个时候改动的数据很少,已经趋于排好了
for i in range(gap,len(li)): # 用冒泡排序作为间隔gap的排序,也可以插入排或选择排
for j in range(len(li)-1,i-1,-1):
if li[j-gap] > li[j]:
li[j],li[j-1] = li[j-1],li[j]
gap = gap//2 # 每次gap减小,趋于有序
return li
桶排序
# 桶排序,升序
def bucket_sort(li):
bucket_num = 10 # 桶的数量
max_num = max(li)
buckets = [[] for _ in range(bucket_num)] # 10个桶
bucket_len = max_num//bucket_num+1 # 每个桶的长度,可能不一样,但没有影响;+1是因为取整会漏掉一些数
for i in li:
bucket_ind = i//bucket_len
buckets[bucket_ind].append(i)
li = []
for i in buckets: # 对每一个桶排序
for j in range(0,len(i)): # 这里是选择排序,也可以直接调用其他排序方法
for loop in range(j,len(i)):
if i[j] > i[loop]: # 取无序区的最大值,交换,但这样不稳定
i[j],i[loop] = i[loop],i[j]
li.extend(i)
return li
基数排序
# 基数排序,升序,无需比较
def radix_sort(li):
max_num = max(li)
bucket_num = 10 # 0~9一共10个数字
it = 1
while max_num >= 1:
buckets = [[] for _ in range(bucket_num)] # 每一轮都需要重新定义buckets
for i in li: # 所有元素进入桶
bucket_ind = i%(10**it)//(10**(it-1)) # 取对应位置上的数,比如个位,十位,百位,越先取的优先级越低
buckets[bucket_ind].append(i)
li = []
for i in buckets: # 将数全部取出放入li中
li.extend(i)
it += 1
max_num //= 10 # 标志,当max_num为0时全部位数已经用过了
return li