我知道经典的递归方法是通过合并来排序。
它产生了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)
。我的错误在哪里?
答案 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]