Fromg Google的Python类:
E. Given two lists sorted in increasing order, create and return a merged
list of all the elements in sorted order. You may modify the passed in lists.
Ideally, the solution should work in "linear" time, making a single
pass of both lists.
这是我的解决方案:
def linear_merge(list1, list2):
merged_list = []
i = 0
j = 0
while True:
if i == len(list1):
return merged_list + list2[j:]
if j == len(list2):
return merged_list + list1[i:]
if list1[i] <= list2[j]:
merged_list.append(list1[i])
i += 1
else:
merged_list.append(list2[j])
j += 1
首先,可以在这里使用无限循环吗?当我完成合并列表时,我是否应该使用break关键字中断循环,或者返回是否可以在这里?
我在这里看到过类似的问题,并且所有的解决方案看起来与我的非常相似,即非常类似于C。有没有更像python的解决方案?或者这是因为算法的性质?
答案 0 :(得分:10)
This question比您可能需要的更详细地介绍了这一点。 ;)所选答案符合您的要求。如果我自己需要这样做,我会按照他或她的回答中描述的方式(将列表添加到一起,对新列表进行排序)来实现,因为它非常简单。
修改强>
我在下面添加了一个实现。我实际上在另一个答案中看到了这个似乎已被删除。我只是希望它不会被删除,因为它有一个我没有抓到的错误。 ;)
def mergeSortedLists(a, b):
l = []
while a and b:
if a[0] < b[0]:
l.append(a.pop(0))
else:
l.append(b.pop(0))
return l + a + b
答案 1 :(得分:10)
这是一种发电机方法。您可能已经注意到,很多这些“生成列表”可以作为生成器函数完成。它们非常有用:它们不需要您在使用数据之前生成整个列表,将整个列表保存在内存中,您可以使用它们直接生成许多数据类型,而不仅仅是列表。
如果传递任何迭代器,而不仅仅是列表。
这种方法也通过了一个更有用的测试:它在传递无限或接近无限的迭代器时表现良好,例如。 linear_merge(xrange(10**9), xrange(10**9))
。
这两种情况下的冗余可能会减少,如果您想支持合并两个以上的列表,这将非常有用,但为了清楚起见,我没有在此处这样做。
def linear_merge(list1, list2):
"""
>>> a = [1, 3, 5, 7]
>>> b = [2, 4, 6, 8]
>>> [i for i in linear_merge(a, b)]
[1, 2, 3, 4, 5, 6, 7, 8]
>>> [i for i in linear_merge(b, a)]
[1, 2, 3, 4, 5, 6, 7, 8]
>>> a = [1, 2, 2, 3]
>>> b = [2, 2, 4, 4]
>>> [i for i in linear_merge(a, b)]
[1, 2, 2, 2, 2, 3, 4, 4]
"""
list1 = iter(list1)
list2 = iter(list2)
value1 = next(list1)
value2 = next(list2)
# We'll normally exit this loop from a next() call raising StopIteration, which is
# how a generator function exits anyway.
while True:
if value1 <= value2:
# Yield the lower value.
yield value1
try:
# Grab the next value from list1.
value1 = next(list1)
except StopIteration:
# list1 is empty. Yield the last value we received from list2, then
# yield the rest of list2.
yield value2
while True:
yield next(list2)
else:
yield value2
try:
value2 = next(list2)
except StopIteration:
# list2 is empty.
yield value1
while True:
yield next(list1)
答案 2 :(得分:3)
这是我的基于生成器的实现,它以线性时间合并任意数量的已排序迭代器。
我不确定为什么这样的东西不在itertools中......
def merge(*sortedlists):
# Create a list of tuples containing each iterator and its first value
iterlist = [[i,i.next()] for i in [iter(j) for j in sortedlists]]
# Perform an initial sort of each iterator's first value
iterlist.sort(key=lambda x: x[1])
# Helper function to move the larger first item to its proper position
def reorder(iterlist, i):
if i == len(iterlist) or iterlist[0][1] < iterlist[i][1]:
iterlist.insert(i-1,iterlist.pop(0))
else:
reorder(iterlist,i+1)
while True:
if len(iterlist):
# Reorder the list if the 1st element has grown larger than the 2nd
if len(iterlist) > 1 and iterlist[0][1] > iterlist[1][1]:
reorder(iterlist, 1)
yield iterlist[0][1]
# try to pull the next value from the current iterator
try:
iterlist[0][1] = iterlist[0][0].next()
except StopIteration:
del iterlist[0]
else:
break
以下是一个例子:
x = [1,10,20,33,99]
y = [3,11,20,99,1001]
z = [3,5,7,70,1002]
[i for i in merge(x,y,z)]
答案 3 :(得分:2)
嗨,我刚做了这个练习,我想知道为什么不用,
def linear_merge(list1, list2):
return sorted(list1 + list2)
pythons排序函数是线性的不是吗?
答案 4 :(得分:1)
我同意其他答案,扩展和排序是最简单的方法,但是如果你必须合并,这会更快一点,因为它不会在每次迭代时调用len
,也不会执行边界检查。 Python模式,如果你可以这样称呼的话,就是避免测试一个罕见的情况并改为捕获异常。
def linear_merge(list1, list2):
merged_list = []
i = 0
j = 0
try:
while True:
if list1[i] <= list2[j]:
merged_list.append(list1[i])
i += 1
else:
merged_list.append(list2[j])
j += 1
except IndexError:
if i == len(list1):
merged_list.extend(list2[j:])
if j == len(list2):
merged_list.extend(list1[i:])
return merged_list
修改强>
根据John Machin的评论进行了优化。在try
之外移动while True
并在例外情况下移动merged_list
。
答案 5 :(得分:1)
以下是previous question的实施方式:
def merge(*args):
import copy
def merge_lists(left, right):
result = []
while (len(left) and len(right)):
which_list = (left if left[0] <= right[0] else right)
result.append(which_list.pop(0))
return result + left + right
lists = [arg for arg in args]
while len(lists) > 1:
left, right = copy.copy(lists.pop(0)), copy.copy(lists.pop(0))
result = merge_lists(left, right)
lists.append(result)
return lists.pop(0)
答案 6 :(得分:1)
另一个发电机:
def merge(xs, ys):
xs = iter(xs)
ys = iter(ys)
try:
y = next(ys)
except StopIteration:
for x in xs:
yield x
raise StopIteration
while True:
for x in xs:
if x > y:
yield y
break
yield x
else:
yield y
for y in ys:
yield y
break
xs, ys, y = ys, xs, x
答案 7 :(得分:0)
根据这里的说明:
# Note: the solution above is kind of cute, but unforunately list.pop(0)
# is not constant time with the standard python list implementation, so
# the above is not strictly linear time.
# An alternate approach uses pop(-1) to remove the endmost elements
# from each list, building a solution list which is backwards.
# Then use reversed() to put the result back in the correct order. That
# solution works in linear time, but is more ugly.
和此链接http://www.ics.uci.edu/~pattis/ICS-33/lectures/complexitypython.txt
追加是O(1),反向是O(n)但是它也说pop是O(n)所以哪个是哪个?无论如何,我已经修改了使用pop(-1)的接受答案:
def linear_merge(list1, list2):
# +++your code here+++
ret = []
while list1 and list2:
if list1[-1] > list2[-1]:
ret.append(list1.pop(-1))
else:
ret.append(list2.pop(-1))
ret.reverse()
return list1 + list2 + ret
答案 8 :(得分:0)
此解决方案以线性时间运行,无需编辑l1和l2:
def merge(l1, l2):
m, m2 = len(l1), len(l2)
newList = []
l, r = 0, 0
while l < m and r < m2:
if l1[l] < l2[r]:
newList.append(l1[l])
l += 1
else:
newList.append(l2[r])
r += 1
return newList + l1[l:] + l2[r:]