归并排序:
首先介绍并实现归并算法:
假设列表以某个下标为界,两侧分别有序,则可以使用两个指针,分别比较两侧最小的数,若左侧最小的数小于右侧,则将左侧该数取出,指针向后滑动一个位置,继续比较,直到有一侧没有数,将另一侧剩余的数写入即可,因为两侧均有序。
def merge(li, low, mid, high):
"""
:param li: 传入列表
:param low: 前半段开始位置
:param mid: 前半段结束位置
:param high: 后半段结束位置
:return:
"""
i = low
j = mid + 1
tli = []
while i <= mid and j <= high : # 两侧都有数
if li[i] > li[j] :
tli.append(li[j])
j += 1
print(f"right,j = {j}, tli = {tli}")
else :
tli.append(li[i])
i += 1
print(f"left,i = {i}, tli = {tli}")
# 此时至少有一侧是没有数的
while i <= mid :
tli.append(li[i])
i += 1
print(f"left,i = {i}, tli = {tli}")
while j <= high :
tli.append(li[j])
j += 1
print(f"right,j = {j}, tli = {tli}")
li[low: high + 1] = tli
s = [1, 6, 7, 2, 3, 5, 8, 9]
merge(s, 0, 2, 7)
print(s)
left,i = 1, tli = [1]
right,j = 4, tli = [1, 2]
right,j = 5, tli = [1, 2, 3]
right,j = 6, tli = [1, 2, 3, 5]
left,i = 2, tli = [1, 2, 3, 5, 6]
left,i = 3, tli = [1, 2, 3, 5, 6, 7]
right,j = 7, tli = [1, 2, 3, 5, 6, 7, 8]
right,j = 8, tli = [1, 2, 3, 5, 6, 7, 8, 9]
[1, 2, 3, 5, 6, 7, 8, 9]
归并排序思路:
一个数或者零个数一定是有序的,所以先将列表不断二分,直到一个数或零个数,这样每个子列表是有序的,将两个子列表合并后,达到了归并的条件,即左右两侧都是有序的,继续合并直到整个列表有序。
则可以将列表看作三部分
有序的列表左侧;
有序的列表右侧;
对列表归并;
使用递归:
m = 0
def merge(li, low, mid, high):
"""
:param li: 传入列表
:param low: 前半段开始位置
:param mid: 前半段结束位置
:param high: 后半段结束位置
:return:
"""
global m
m += 1
i = low
j = mid + 1
tli = []
print(f"第{m}次调用merge函数:")
print(f"low = {low}, mid = {mid}, high = {high}")
print("all : ")
print(li)
print("将要归并的范围:")
print(li[low: high + 1])
while i <= mid and j <= high : # 两侧都有数
if li[i] > li[j] :
tli.append(li[j])
j += 1
else :
tli.append(li[i])
i += 1
# 此时至少有一侧是没有数的
while i <= mid :
tli.append(li[i])
i += 1
while j <= high :
tli.append(li[j])
j += 1
print("范围归并后:")
print(tli)
li[low: high + 1] = tli
print("归并后的总列表")
print(li)
print("******************************************************************")
def merge_sort(li, low, high):
if low < high : # 即列表中至少还有两个元素
mid = (low + high) // 2
merge_sort(li, low, mid)
merge_sort(li, mid + 1, high)
merge(li, low, mid, high)
s = list(range(17))
import random
random.shuffle(s)
print(s)
merge_sort(s, 0, len(s) - 1)
print(s)
[16, 4, 7, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
第1次调用merge函数:
low = 0, mid = 0, high = 1
all :
[16, 4, 7, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[16, 4]
范围归并后:
[4, 16]
归并后的总列表
[4, 16, 7, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第2次调用merge函数:
low = 0, mid = 1, high = 2
all :
[4, 16, 7, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[4, 16, 7]
范围归并后:
[4, 7, 16]
归并后的总列表
[4, 7, 16, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第3次调用merge函数:
low = 3, mid = 3, high = 4
all :
[4, 7, 16, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[0, 2]
范围归并后:
[0, 2]
归并后的总列表
[4, 7, 16, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第4次调用merge函数:
low = 0, mid = 2, high = 4
all :
[4, 7, 16, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[4, 7, 16, 0, 2]
范围归并后:
[0, 2, 4, 7, 16]
归并后的总列表
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第5次调用merge函数:
low = 5, mid = 5, high = 6
all :
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[3, 6]
范围归并后:
[3, 6]
归并后的总列表
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第6次调用merge函数:
low = 7, mid = 7, high = 8
all :
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[12, 15]
范围归并后:
[12, 15]
归并后的总列表
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第7次调用merge函数:
low = 5, mid = 6, high = 8
all :
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[3, 6, 12, 15]
范围归并后:
[3, 6, 12, 15]
归并后的总列表
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第8次调用merge函数:
low = 0, mid = 4, high = 8
all :
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[0, 2, 4, 7, 16, 3, 6, 12, 15]
范围归并后:
[0, 2, 3, 4, 6, 7, 12, 15, 16]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第9次调用merge函数:
low = 9, mid = 9, high = 10
all :
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[1, 14]
范围归并后:
[1, 14]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第10次调用merge函数:
low = 11, mid = 11, high = 12
all :
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[10, 11]
范围归并后:
[10, 11]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第11次调用merge函数:
low = 9, mid = 10, high = 12
all :
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[1, 14, 10, 11]
范围归并后:
[1, 10, 11, 14]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 13, 9, 5, 8]
******************************************************************
第12次调用merge函数:
low = 13, mid = 13, high = 14
all :
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 13, 9, 5, 8]
将要归并的范围:
[13, 9]
范围归并后:
[9, 13]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 9, 13, 5, 8]
******************************************************************
第13次调用merge函数:
low = 15, mid = 15, high = 16
all :
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 9, 13, 5, 8]
将要归并的范围:
[5, 8]
范围归并后:
[5, 8]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 9, 13, 5, 8]
******************************************************************
第14次调用merge函数:
low = 13, mid = 14, high = 16
all :
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 9, 13, 5, 8]
将要归并的范围:
[9, 13, 5, 8]
范围归并后:
[5, 8, 9, 13]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 5, 8, 9, 13]
******************************************************************
第15次调用merge函数:
low = 9, mid = 12, high = 16
all :
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 5, 8, 9, 13]
将要归并的范围:
[1, 10, 11, 14, 5, 8, 9, 13]
范围归并后:
[1, 5, 8, 9, 10, 11, 13, 14]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 5, 8, 9, 10, 11, 13, 14]
******************************************************************
第16次调用merge函数:
low = 0, mid = 8, high = 16
all :
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 5, 8, 9, 10, 11, 13, 14]
将要归并的范围:
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 5, 8, 9, 10, 11, 13, 14]
范围归并后:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
归并后的总列表
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
******************************************************************
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
进程已结束,退出代码0
在上述代码中,我加入了多项打印函数帮助理解递归的调用过程,通过观察可以发现,此时我要排序的列表长度为17,最大下标为16,则第一次的mid为8,在第8次调用merge时,完成了对列表前半部分(左侧0~8)共9位数字的排序,之后low从9开始,对右侧的数字排序。
在第16次调用merge时,已经与文章开头的归并排序算法一致。
时间复杂度:O(nlogn)
将列表二分logn
二分之后遍历列表n
空间复杂度:O(n)
在merge中开辟了新的列表用于储存数据
以下对学习过的排序方法进行了比较:
空间复杂度算入了递归占用的系统空间
稳定性是指在两个数据一样时,是否进行交换
挨个比较大小并交换的是稳定的
遍历后进行位置交换的是不稳定的
以上,完成了常见的排序算法的学习笔记。