迭代合并排序?

时间:2019-03-09 11:15:22

标签: python algorithm sorting time-complexity mergesort

我知道经典的递归方法是通过合并来排序。 它产生了O(n * log(n))复杂性,可以通过递归关系轻松地显示出来。

我尝试以迭代方式重新实现合并排序:

def atomize(l):
    return list(
        map(
            lambda x: [x],
            l if l is not None else []
        )
    )

def merge(l, r):
    res = []
    while (len(l) + len(r)) > 0:
        if len(l) < 1:
            res += r
            r = []
        elif len(r) < 1:
            res += l
            l = []
        else:
            if l[0] <= r[0]:
                res.append(l.pop(0))
            else:
                res.append(r.pop(0))
    return res

def iter_merge_sort(l):
    atoms = atomize(l) # O(n)
    while len(atoms) > 1: # O(n - 1)
        atoms.append(merge(atoms.pop(0), atoms.pop(0)))
    return atoms[0]

...感觉好像我在某个地方弄错了,但是我没注意到确切的位置。递归合并排序拆分问题,除非未排序的值列表减少为单例列表-可以比较的单个元素。这就是atomize(...)的作用:给定一个列表,产生一个列表单子列表(顺序O(n))。

很显然,merge(...)也是O(n):暂时不要忽略没有用于链接的链表,这在这里并不重要。

最后,。while本身的iter_merge_sort(...)块正好进行n - 1个重复,每个重复最多花费O(n)。因此,我采用了O(n * log(n))算法,并将其“改进”为(n - 1) * n ~ O(n * n)。我的错误在哪里?

1 个答案:

答案 0 :(得分:1)

您的算法完全正确。问题在于您使用list.pop(0)作为出队的方式,在Python中这花费了 O(n),因为列表的弹出项目之后的所有项目都必须复制到前面的位置。

您可以使用collections.deque代替列表,以便可以使用deque.popleft方法,该方法的成本为 O(1)

from collections import deque

def atomize(l):
    return deque(
        map(
            lambda x: deque([x]),
            l if l is not None else []
        )
    )

def merge(l, r):
    res = deque()
    while (len(l) + len(r)) > 0:
        if len(l) < 1:
            res += r
            r = deque()
        elif len(r) < 1:
            res += l
            l = deque()
        else:
            if l[0] <= r[0]:
                res.append(l.popleft())
            else:
                res.append(r.popleft())
    return res

def iter_merge_sort(l):
    atoms = atomize(l) # O(n)
    while len(atoms) > 1: # O(n - 1)
        atoms.append(merge(atoms.popleft(), atoms.popleft()))
    return list(atoms[0])

这样:

iter_merge_sort([3,5,1,6,2,1])

返回:

[1, 1, 2, 3, 5, 6]