目录
一. 问题描述
二. 标准归并排序算法
2.1 基本思想
分治策略求解:
- 将原始数据A中的元素分成两个子集合:A[1:n/2]和A[n/2:];
- 分别对这两个子集合单独排序;
- 将已排序的两个子序列归并成一个含有n个元素的排序好的序列。
这样的一个排序过程称为归并排序(Merge sort)。
2.2 时间复杂度分析
在归并排序中,Merge过程(即合并操作)花费时间与两数组元素总数成正比。(可表示为cn,其中c为某正常数,n为元素数)。原因是每次从两数组A和B中取出一个最小值minv,取出的过程需要A[0]和B[0]两个元素的比较,其中A、B非降序排列。
则归并排序MergeSort的排序时间用递推关系式表示如下:
结论:标准归并排序的时间复杂度为O(nlogn).
2.3 代码实现
# Merge sort
def Merge(a1,a2):
i,j = 0,0
a = []
while i < len(a1) and j < len(a2):
if a1[i] < a2[j]:
a += [a1[i]]
i += 1
else:
a += [a2[j]]
j += 1
if i == len(a1):
a += a2[j:]
else:
a += a1[i:]
return a
def Merge_sort(a):
if len(a) == 1:
return a
elif len(a) == 2:
if a[0]>a[1]:
return [a[1],a[0]]
else:
return a
else:
mid = math.ceil(len(a)/2)
a1,a2 = Merge_sort(a[0:mid]),Merge_sort(a[mid:])
return Merge(a1,a2)
三. 根号n段归并排序算法
3.1 时间复杂度分析
3.2 k路归并算法
参考:Tournament Tree (Winner Tree) and Binary Heap - GeeksforGeeks
3.3 Python代码实现根号n段归并排序算法
Note:下面代码可以实现功能,但太丑了,后续待改进
def k_merge(A:list):
result = []
k = len(A)
depth = math.ceil(math.log2(k))
# 用inf或-inf补全Winner Tree,保证len(A) = 2**depth
# 由于本例中,每次取出最小值,故用inf补全
for i in A:
i.append(math.inf)
for i in range(2**depth - k):
A.append([math.inf])
# 构建 Winner Tree:Winner Tree[1]对应根节点
# Note:Winner Tree中保存的时子序列号,不是元素值
# 共有1-(2**depth-1)个结点,这里多了1个,目的是让下标为1对应开始序号1
Winner_Tree = [0]*(2**depth)
# 先构建最下面1层
j = 0
for i in range(2**(depth-1),2**depth):
if A[j][0] < A[j+1][0]:
Winner_Tree[i] = j
else:
Winner_Tree[i] = j+1
j += 2
# 还有depth-1层没有构建
for i in range(depth-1,0,-1):
son_tree = Winner_Tree[2**i:2**(i+1)]
# print(son_tree)
begin = 0
for j in range(2**(i-1),2**i):
# print(A[son_tree[begin]][0])
# print(A[son_tree[begin+1]][0])
c = 1
if A[son_tree[begin]][0] < A[son_tree[begin+1]][0]:
Winner_Tree[j] = son_tree[begin]
else:
Winner_Tree[j] = son_tree[begin+1]
begin += 2
# print(Winner_Tree)
# k个子序列中,第一个元素最小的k索引
mink = Winner_Tree[1]
while A[mink][0] != inf:
result.append(A[mink][0])
A[mink].pop(0)
if mink%2 == 0:
partner = mink+1
else:
partner = mink-1
if A[mink][0] < A[partner][0]:
new = mink
else:
new = partner
if new == 0:
junction = 2**(depth-1)
else:
junction = 2**(depth-1) + int(math.log2(new))
Winner_Tree[junction] = new
# father是结点编号
j = depth - 1
while junction != 1:
father = 2**(j-1) + int((junction-2**j)/2)
if junction%2 == 0:
partner = junction + 1
else:
partner = junction - 1
if A[Winner_Tree[junction]][0] < A[Winner_Tree[partner]][0]:
Winner_Tree[father] = Winner_Tree[junction]
else:
Winner_Tree[father] = Winner_Tree[partner]
junction = father
j -= 1
mink = Winner_Tree[1]
return result
def sqrt_Merge_sort(a:list):
if len(a) == 1:
return a
elif len(a) == 2:
if a[0]>a[1]:
return [a[1],a[0]]
else:
return a
# 这里一定要认定3为基本解之一:因为根号3下取整为1,根号4下取整为2,因此3作为1个序列来看
elif len(a) == 3:
a = a.copy()
p,q = min(a),max(a)
a.remove(p)
a.remove(q)
return [p,a[0],q]
else:
# 划分为dif个子序列 如:长度为8的序列,分为2个子序列
dif = math.floor(math.sqrt(len(a)))
A = []
for i in range(dif-1):
A.append(a[dif*i:dif*(i+1)])
A.append(a[dif*(i+1):])
# 每个子序列排序
A = [sqrt_Merge_sort(i) for i in A]
# 合并
res = k_merge(A)
return res