本文章只展示代码实现 ,原理大家可以参考:
https://zhuanlan.zhihu.com/p/42586566
一、冒泡排序
def bubble_sort(lst):
for i in range(len(lst) - 1): # 表示第i趟
exchange = False # 每一趟做标记
for j in range(len(lst)-i-1): # 表示箭头
if lst[j] > lst[j+1]: # 此时是升序排序,>改为<则改为了降序
lst[j],lst[j+1] = lst[j+1],lst[j]
exchange = True # 进行了交换,exchange标记为Ture
print(f"第{i+1}趟后的列表为:{lst}") # 查看排序过程
if not exchange: # 如果没有进行交换,直接返回,优化的步骤
return
lst2 = [1,2,3,8,7,6,5]
print("***改进后的冒泡排序***")
print(f"初始列表:{lst2}")
bubble_sort(lst2)
# 输出结果
# 初始列表:[1, 2, 3, 8, 7, 6, 5]
# 第1趟后的列表为:[1, 2, 3, 7, 6, 5, 8]
# 第2趟后的列表为:[1, 2, 3, 6, 5, 7, 8]
# 第3趟后的列表为:[1, 2, 3, 5, 6, 7, 8]
# 第4趟后的列表为:[1, 2, 3, 5, 6, 7, 8]
二、选择排序
def select_sort(lst):
for i in range(len(lst) - 1): # i代表第几趟
min_location = i # 最小位置的标记,第一次默认最小的数为无序区的第一个,即下标为i
for j in range(i+1,len(lst)): # 从i开始相当于自己和自己比了一次,此步骤多余,因此从i+1开始
if lst[j] < lst[min_location]:
min_location = j
lst[i],lst[min_location] = lst[min_location],lst[i] # 最小的值和有序区的最后一个值进行交换
print(f"第{i + 1}趟后的列表为:{lst}")
lst1 = [3,2,4,13,11,8]
select_sort(lst1)
# 结果
# 第1趟后的列表为:[2, 3, 4, 13, 11, 8]
# 第2趟后的列表为:[2, 3, 4, 13, 11, 8]
# 第3趟后的列表为:[2, 3, 4, 13, 11, 8]
# 第4趟后的列表为:[2, 3, 4, 8, 11, 13]
# 第5趟后的列表为:[2, 3, 4, 8, 11, 13]
三、插入排序
def insert_sort(lst):
for i in range(1,len(lst)): # i表示摸到的牌的下标
tmp = lst[i] # tmp代表摸到的牌
j = i - 1 # j代表的是手里的牌的下标,手上自动已有第一张牌
while lst[j] > tmp and j >= 0: # 需要移动有序区牌的情况
lst[j+1] = lst[j]
j -= 1
lst[j+1] = tmp # lst[j+1]是用来存放要插入的牌
print(f"第{i}趟的列表:{lst}")
lst1 = [3,2,5,8,6,9,7]
print(f"原列表{lst1}")
insert_sort(lst1)
# 结果原列表[3, 2, 5, 8, 6, 9, 7]
# 第1趟的列表:[2, 3, 5, 8, 6, 9, 7]
# 第2趟的列表:[2, 3, 5, 8, 6, 9, 7]
# 第3趟的列表:[2, 3, 5, 8, 6, 9, 7]
# 第4趟的列表:[2, 3, 5, 6, 8, 9, 7]
# 第5趟的列表:[2, 3, 5, 6, 8, 9, 7]
# 第6趟的列表:[2, 3, 5, 6, 7, 8, 9]
四、快速排序
def partition(lst,left,right): # partition(分割)函数
tmp = lst[left] # 存放基准点,以最左边的值为例
while left < right: # 当左边的位置(下标)小于右边的时候,说明还有至少两个元素,则进行排序
while lst[right] >= tmp and left < right: # 从右边找比tmp小的元素
right -= 1 # 比tmp大,则往左移动一位
lst[left] = lst[right] # 如果出现lst[right] < tmp,则将右边的值lst[right]写到左边的空位lst[left]
# print(f"从右边找比tmp小的数后的列表:{lst}")
while lst[left] <= tmp and left < right: # 从左边找比tmp大的元素lst[left],放到右边的空位上lst[right]
left += 1
lst[right] = lst[left]
# print(f"从左边找比tmp大的数后的列表:{lst}")
lst[left] = tmp # 最后把tmp归位
return left # 返回mid的值
# lst1 = [5,7,4,2,6,8,3,1,9]
# print(f"分割前的列表{lst1}")
# partition(lst1,0,len(lst1)-1)
# print(f"最终tmp归为后的列表:{lst1}")
# 输出结果
# 分割前的列表[5, 7, 4, 2, 6, 8, 3, 1, 9]
# 从右边找比tmp小的数后的列表:[1, 7, 4, 2, 6, 8, 3, 1, 9]
# 从左边找比tmp大的数后的列表:[1, 7, 4, 2, 6, 8, 3, 7, 9]
# 从右边找比tmp小的数后的列表:[1, 3, 4, 2, 6, 8, 3, 7, 9]
# 从左边找比tmp大的数后的列表:[1, 3, 4, 2, 6, 8, 6, 7, 9]
# 从右边找比tmp小的数后的列表:[1, 3, 4, 2, 6, 8, 6, 7, 9]
# 从左边找比tmp大的数后的列表:[1, 3, 4, 2, 6, 8, 6, 7, 9]
# 最终tmp归为后的列表:[1, 3, 4, 2, 5, 8, 6, 7, 9]
# 随后完成快速排序主体部分的代码
def quick_sort(lst, left, right): # 需要传入一个列表lst,以及最左边后最后边的位置
if left < right: # 如果左边小于右边,则说明列表内至少有两个元素
mid = partition(lst, left, right) # 通过partition获得基准值mid
quick_sort(lst, left, mid - 1) # 递归左边的元素
quick_sort(lst, mid + 1, right) # 递归右边的元素
lst2 = [5,7,4,2,6,8,3,1,9]
print(f"初始列表:{lst2}")
quick_sort(lst2,0,len(lst2)-1)
print(f"快速排序后的{lst2}")
# 输出结果
# 初始列表:[5, 7, 4, 2, 6, 8, 3, 1, 9]
# 快速排序后的[1, 2, 3, 4, 5, 6, 7, 8, 9]
五、堆排序
我们以大根堆为例,因为大根堆排序出来的结果是升序。
# 向下调整函数
def shift(lst,low,high): # low:对根节点的位置;high:堆最后一个元素的位置
i = low # 标记low
j = 2 * i + 1 # j代表左孩子位置
tmp = lst[low] # 把堆暂时顶存起来
while j <= high: # 只要j位置大于high就说明没有元素了,循环就停止,所欲j<=high时就代表有元素,就循环
if j + 1 <= high and lst[j+1] > lst[j]: # 首先判断是否j这一层有右孩子(j + 1直的j这一层的另一个数),其次判断j这一层元素的大小,j+1(右孩子)大于j(左孩子),则j指向j+1
j = j + 1 # j指向右孩子
if lst[j] > tmp: # 然后判断j和堆顶的元素(tmp)的大小,如果j位置的元素大于堆顶元素,则堆顶元素和j(左孩子)位置互换
lst[i] = lst[j]
i = j # low堆顶的位置指向i,继续看下一层
j = 2 * i + 1 # 同时j指向下一层的左孩子
else: # tmp最大,则把tmp放到i的位置上
lst[i] = tmp # 把tmp放到某一级
break
else:
lst[i] = tmp # 把tmp放到叶子节点上
# 堆排序主函数
def heap_sort(lst):
n = len(lst) # 获取列表长度
# 先建堆
for i in range((n-2)//2,-1,-1): #从最后一个根节点,到最上面的根节点
# i代表建堆时调整部分的根的下标,(n-2)//2是根到位置,n-1是孩子节点下标,(n-1-1)//2代表根节点的下标,-1是最后的根节点位置(0),那么range就是-1
shift(lst,i,n-1) # i为堆顶,high为最后一个节点n-1
# 建堆完成
# print(lst) # 检验建堆是否完成
# 检验建堆是否成功
# lst = [i for i in range(10)]
# import random
# random.shuffle(lst)
# print(lst)
# heap_sort(lst)
# 结果
# [2, 3, 9, 7, 1, 8, 6, 0, 5, 4]
# [9, 7, 8, 5, 4, 2, 6, 0, 3, 1]
# 接下来“农村包围城市”,从最后一个节点开始
for i in range(n-1,-1,-1): # i指向最后一个节点
lst[0],lst[i] = lst[i],lst[0] # 堆顶元素lst[0]和最后一个节点位置互换
shift(lst,0,i - 1) # i - 1代表新的high
# return lst
lst1 = [i for i in range(10)]
import random
random.shuffle(lst1)
print(f"初始列表{lst1}")
heap_sort(lst1)
print(lst1)
# 结果
# 初始列表[2, 1, 8, 4, 6, 3, 7, 5, 9, 0]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
python中堆排序的内置模块
import heapq # q-->queue 优先队列(小的或大的先出)
import random
lst2 = [i for i in range(10)]
random.shuffle(lst2)
print(f"初始列表:{lst2}")
heapq.heapify(lst2) # 建堆,建的是小根堆
for i in range(len(lst2)):
print(heapq.heappop(lst2),end=",") # heappop每次弹出一个最小的元素
堆排序解决topk问题
现在有n个数,需要设计算法得到前k大的数。(k<n)
import random
def shift(lst,low,high): # low:对根节点的位置;high:堆最后一个元素的位置
i = low # 标记low
j = 2 * i + 1 # j代表左孩子位置
tmp = lst[low] # 把堆顶存起来
while j <= high: # 只要j位置有元素,就循环
if j + 1 <= high and lst[j+1] < lst[j]: # 首先判断是否j这一层有右孩子(j + 1直的j这一层的另一个数),其次判断j这一层元素的大小,j+1(有孩子)大于j,则j指向j+1
j = j + 1 # j指向有孩子
if lst[j] < tmp: # 然后判断j和堆顶的元素(tmp)的大小,如果j位置的元素大于堆顶元素,则堆顶元素和j(左孩子)位置互换
lst[i] = lst[j]
i = j # 继续看下一层
j = 2 * i + 1
else: # tmp最大,则把tmp放到i的位置上
lst[i] = tmp # 把tmp放到某一级
break
lst[i] = tmp # 把tmp放到叶子节点上
# topk
def topk(lst,k):
heap = lst[0:k]
for i in range((k-2)//2,-1,-1):
shift(heap,i,k-1)
# 1.建堆完成
for i in range(k,len(lst)-1):
if lst[i] > heap[0]:
heap[0] = lst[i]
shift(heap,0,k-1)
# 2.遍历
for i in range(k-1,-1,-1):
heap[0],heap[i] = heap[i],heap[0]
shift(heap,0,i-1)
# 3.出数
return heap
lst1 = [i for i in range(10)]
random.shuffle(lst1)
print(f"初始列表{lst1}")
result = topk(lst1,5)
print(result)
# 结果
# 初始列表[1, 8, 7, 2, 6, 3, 0, 9, 5, 4]
# [9, 8, 7, 6, 5]
六、归并算法
# 已有两个有序区的归并函数
def merge(lst,low,mid,high):
i = low # 左边有序区的标记位
j = mid + 1 # 右边有序区的标记位
tmp_lst = [] # 存放比较大小之后的数
while i <= mid and j <= high: # 只要左右两边有序区都有数
if lst[i] < lst[j]: # 依次判断左右两个有序区的元素大小
tmp_lst.append(lst[i])
i += 1
else:
tmp_lst.append(lst[j])
j += 1
# while执行完,肯定有一个有序区没有元素了
while i <= mid: # 左边有序区还有元素情况,则把剩下元素增加到tmp_lst
tmp_lst.append(lst[i])
i += 1
while j <= high: # 右边有序区还有元素,则把剩下元素增加到tmp_lst
tmp_lst.append(lst[j])
j += 1
lst[low:high+1] = tmp_lst # 把tmp_lst写回去,low是0,high+1因为遍历会比长度小1
# lst2 = [2,4,5,7,9,1,3,6,8]
# merge(lst2,0,4,8)
# print(lst2)
# 输出结果
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 归并排序主函数
def merge_sort(lst,low,high): # low:下标最小的;high:下标最大的
if low < high: # 至少有两个元素
mid = (low+high)//2
merge_sort(lst,low,mid) # 递归左边化成有序区
merge_sort(lst,mid+1,high) # 递归右边化成有序区
merge(lst,low,mid,high) # 归并两个有序区
lst1 = [i for i in range(20)]
import random
random.shuffle(lst1)
print(f"初始列表:{lst1}")
merge_sort(lst1,0,len(lst1)-1)
print(lst1)
# 输出结果初始列表:
# [1, 9, 15, 18, 2, 16, 11, 0, 8, 4, 12, 13, 14, 19, 3, 10, 5, 7, 17, 6]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
七、希尔排序
# 因为希尔排序是利用了插入排序的思想,因此我们可以在插入排序算法的基础上改
def insert_sort_gap(lst,gap): # gap即为d,即间隔
for i in range(gap,len(lst)): # 从gap开始
tmp = lst[i] #
j = i - gap # j代表的是手里的牌的下标,换为希尔排序,那么就是和gap距离的元素相比较
while lst[j] > tmp and j >= 0:
# 说明lst[i-gap]>lst[i],即需要调整元素的情况,即比如实例中的3(lst[i])和5(lst[i-gap]),5>3且5的位置》0进行调整
lst[j+gap] = lst[j] # 那么调整就是把lst[i-gap]赋值给新的lst[i]即lst[j+gap],这样保证了让小的排到前面,最终输出升序
j -= gap
lst[j+gap] = tmp # lst[j+1]是用来存放要插入的牌
def shell_sort(lst):
d = len(lst)//2 # 求d
while d >= 1: # 最终d=1进行最后一次循环,因此d》=1进行循环
insert_sort_gap(lst,d) # 进行插入排序
d //= 2 # 产生下一个d
print(lst)
# 检测希尔排序
lst1 = [i for i in range(14)]
import random
random.shuffle(lst1)
print(f"{lst1}")
shell_sort(lst1)
# 输出结果
# [0, 2, 10, 7, 4, 9, 11, 6, 8, 12, 13, 1, 5, 3]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
八、计数排序
def count_sort(lst,max_count=100): # 传入列表和最大值
count = [0 for i in range(max_count+1)] # 计数列表
for val in lst:
count[val] += 1 # val即为下标
lst.clear() # 写到lst中之前需要情况lst
for ind,val in enumerate(count): # 下标和值
for i in range(val): # 查看个数并增加到列表中
lst.append(ind)
import random
lst1 = [random.randint(0,5) for i in range(10)]
print(lst1)
count_sort(lst1)
print(lst1)
# 输出结果
# [2, 2, 0, 1, 4, 3, 5, 4, 0, 3]
# [0, 0, 1, 2, 2, 3, 3, 4, 4, 5]
九、桶排序
def bucket_sort(lst, n=100, max_num=10000): # 船体列表和最大值为10000
buckets = [[] for _ in range(n)] # 创建桶
for var in lst:
i = min(var // (max_num // n), n - 1)
# 此时需要考虑如何进桶,i表示var放入几号桶里
buckets[i].append(var) # 把var加到桶里
for j in range(len(buckets[i]) - 1, 0, -1): # 通过插入排序排序桶内元素
if buckets[i][j] < buckets[i][j - 1]:
buckets[i][j], buckets[i][j - 1] = buckets[i][j - 1], buckets[i][j]
else:
break
sorted_lst = []
for buc in buckets:
sorted_lst.extend(buc)
return sorted_lst
import random
lst1 = [random.randint(0, 10000) for i in range(100000)]
# print(lst1)
lst1 = bucket_sort(lst1)
print(lst1)
# 结果
# [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,......]
十、基数排序
def redix_sort(lst):
max_num = max(lst) # 求最大值确定位数,99-->2,888-->3
it = 0 # 用于去10的次方
while 10 ** it <= max_num:
buckets = [[] for _ in range(10)] # 分10个桶
for var in lst: # 进行装桶操作
# 987获取个位:987%10=7,即987//1-->987,987%10-->7;取十位:987//10-->98,98%10-->8;取百位987//100-->9,9%10=9
digit = (var // 10 ** it)% 10
buckets[digit].append(var)
# 分桶完成
lst.clear() # 清空列表
for buc in buckets:
lst.extend(buc) # 把桶内数写回lst
print(lst) # 查看每次分桶
it += 1
import random
lst1 = list(range(20))
random.shuffle(lst1)
print(lst1)
redix_sort(lst1)
print(lst1)
# 输出结果
# [3, 1, 6, 15, 2, 18, 0, 16, 19, 7, 11, 5, 9, 10, 17, 8, 13, 12, 14, 4]
# [0, 10, 1, 11, 2, 12, 3, 13, 14, 4, 15, 5, 6, 16, 7, 17, 18, 8, 19, 9]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]