本文为 快速排序迭代版和递归版 的归并版本。

要点是 merge 的写法,同时支持迭代和递归

递归版:

def merge(S1, S2, result, result_start):
    start, end1, end2 = 0, len(S1), len(S2)
    x, y, z = start, start, result_start
    while (x + y) < end1 + end2:
        if (y == len(S2)) or (x < len(S1) and S1[x] <= S2[y]):
            result[z] = S1[x]
            x += 1
        else:
            result[z] = S2[y]
            y += 1
        z += 1

def merge_sort_recursive(S):
    n = len(S)
    if n < 2: return                
    mid = n // 2
    S1, S2 = S[0:mid], S[mid:n]
    merge_sort_recursive(S1)          
    merge_sort_recursive(S2)        
    merge(S1, S2, S, 0)            

 

迭代版(同一 merge)

def merge_sort_iter(S):
    n = len(S)
    if n < 2: return
    logn = math.ceil(math.log(n, 2))
    src, dest = S, [None] * n                  
    for i in (2**k for k in range(logn)):      
        for j in range(0, n, 2*i):            
            merge(src[j:j+i], src[j+i: min(j + 2 * i, len(src))], dest, j)
        src, dest = dest, src
    if S is not src:
        S[0:n] = src[0:n]

其中 desc 和 src 的交换是作为空间复用。merge 函数 result_start 参数纯属为迭代函数准备。所以好的公共函数,有助于 迭代到递归的转化。

merge(src[j:j+i], src[j+i: min(j + 2 * i, len(src))], dest, j)

中 i  为 step, 一次比较几个元素, j 是这一轮 merge 的起点。

函数是不是确定实现了,没有没有其他 bug。 暂未详细研究。只是用了下面的代码来检验:

a = random.sample(range(30), 30)    
merge_sort_iter(a)     
assert a == list(range(30))

 


 

本着认真负责的态度,加强了验证。

from hypothesis import given, strategies as st

@given(st.lists(st.integers()))
def test_merge_sort_iter(numbers):
    merge_sort_iter(numbers)
    assert all(x <= y for x, y in zip(numbers, numbers[1:]))

说明算法基本上是正确的。 hypothesis 只是构建了部分输入集,并不能完全证明算法实现没有任何问题。但比普通单元测试靠谱。

 

 

jiamo post at 2021-03-05