Python:使用列表理解进行排序合并
我正在尝试将两个排序数组合并为一个排序数组。一般来说,我知道怎么做(所以问题本身不是关于合并列表),但出于学习目的,我想用列表理解来写它。理想情况下,我会这样写:Python:使用列表理解进行排序合并,python,Python,我正在尝试将两个排序数组合并为一个排序数组。一般来说,我知道怎么做(所以问题本身不是关于合并列表),但出于学习目的,我想用列表理解来写它。理想情况下,我会这样写: i=0; j=0 [a[i++] if j >= len(b) or a[i] < b[j] else b[j++] for tmp in range (len(a)+len(b))] 但这不起作用,因为python不通过引用传递。如果我可以查看顶部元素而不推进它,我会使用迭代器,但我不能 那么,有没有一种方法
i=0; j=0
[a[i++] if j >= len(b) or a[i] < b[j] else b[j++]
for tmp in range (len(a)+len(b))]
但这不起作用,因为python不通过引用传递。如果我可以查看顶部元素而不推进它,我会使用迭代器,但我不能
那么,有没有一种方法可以把它优雅地写在一句话中,而不是像这样:
i=0; j=0
while i<len(a) or j<len(b):
if j >= len(b) or a[i]<b[j]: res.append(a[i]); i += 1
else res.append(b[j]); j += 1
i=0;j=0
而ii+1
j+1
如果您需要做的只是增加,而这是您遇到的唯一问题,请检查此答案
[a[i++]如果j>=len(b)或a[i]
假设i和j是带有一个元素的一维列表,如
上面的链接
使用上述函数,可以将代码转换为
[a[增量(i)]如果j>=len(b)或a[i]
更新
>>> def increment(number):
... number[0] += 1
... return number[0]
...
>>> num = [0]
>>> print [increment(num) for dummy in range(10)]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
这就是我所说的优雅:
a = [0,1,2]
b = [1,2,3]
i, j = 0, 0
result = []
for temp in range(len(a)+len(b)):
if j>=len(b) or a[i]<b[j]:
result.append(a[i])
i += 1
else:
result.append(b[j])
j += 1
print(i,j,result)
print(result)
产生:
[0, 1, 1.1, 2.1, 3, 4.1, 5, 6]
它与我以前遇到的问题相同,它不从b
收集尾随值
如果我确切知道需要多少次迭代,我也可以在列表中运行它
result = [a.pop(0) if a[0]<b[0] else b.pop(0) for _ in range(8)]
result=[a.pop(0)如果[0]我认为让列表理解在合并两个列表时起作用的唯一模糊的python方法是编写一个自定义迭代器类型,让您可以查看将要生成的下一个元素。下面是我提供的一个快速版本:
class PeekIterator(object):
def __init__(self, iterable, sentinel=None):
self.iterator = iter(iterable)
self.sentinel = sentinel
self.next_val = self.sentinel
def peek(self):
if self.next_val is self.sentinel:
try:
self.next_val = next(self.iterator)
except StopIteration:
pass # the sentinel will be returned if there's nothing left in the iterator
return self.next_val
def __iter__(self):
return self
def __next__(self):
if self.next_val is self.sentinel:
return next(self.iterator) # StopIteration is deliberately allowed to bubble out!
val = self.next_val
self.next_val = self.sentinel
return val
next = __next__ # Python 2 compatibility
现在列表变成:
a_peek = PeekIterator(a)
b_peek = PeekIterator(b)
merged = [next(a_peek) if a_peek.peek() is not None and
(b_peek.peek() is None or a_peek.peek() <= b_peek.peek())
else next(b_peek)
for _ in range(len(a) + len(b))]
我认为不理解的版本会更容易理解。我知道这是一个很老的问题,你可能已经开始了你的生活;)但我遇到了同样的问题,并用列表理解+闭包解决了它
def merge(left, right):
merge.lhs=0
merge.rhs = 0
def combine(left_side):
if left_side:
el = left[merge.lhs]
merge.lhs += 1
else:
el = right[merge.rhs]
merge.rhs += 1
return el
result = [combine(True) if merge.rhs >= len(right) or (merge.lhs < len(left) and left[merge.lhs] < right[merge.rhs]) else combine(False) for i in range(len(left) + len(right))]
return result
l = [1,3,5]
r = [2,4]
merge(l, r)
def合并(左、右):
merge.lhs=0
merge.rhs=0
def联合收割机(左侧):
如果是左侧:
el=左[merge.lhs]
merge.lhs+=1
其他:
el=右侧[merge.rhs]
merge.rhs+=1
返回el
结果=[如果merge.rhs>=len(右)或(merge.lhs
你可能会说这很难看,我同意,但是,我不确定使用LC是否真的可以合并两个已排序的列表!如第二个答案中所述,使用heapq模块的合并功能可能会重复。这已经完成了你正在尝试完成的任务。这个问题还有其他几种方法可以完成它也是。请不要在一行上写多行语句,也不要在Python中使用分号。以后需要阅读您的代码的人都会感谢您。您是否尝试过sorted(a+b)
。除非您的a
或b
非常大,否则它是O(n log n)可能不重要。例如,对于1000000个项目,排序
需要82ms,而对于这台计算机上的heapq.merge
需要329ms。对于1000000个项目,您的函数需要313ms。这并不能回答问题。这不是需要与列表pop()的平方时间吗方法?关于pop
和列表上的迭代,我喜欢的一点是,我不必一直检查len
。我只是问列表是否为空。编写正确的代码更容易。我可以用迭代器替换pop
。iter(A)
并使用next
。您可以反转两个列表并从后面弹出。这将需要线性时间。如何反向构建列表(例如,首先生成最大结果),然后在返回之前翻转列表?result=[];而a或b:result.append(a.pop()如果是a和(不是b或a[0]>b[0])否则b.pop();返回结果[:-1]
。这仍然是O(N)
,而带有pop(0)
的任何内容都将是二次的。您还需要更改i
和j
的初始化方式,以及列表理解代码的其他部分如何使用它们。a[i]如果i
是一个单元素列表,则不起作用。我探索了迭代器路径,并点击了此peek
问题。没有此额外方法,我必须维护一个单独的当前值
变量。
result = [a.pop(0) if a[0]<b[0] else b.pop(0) for _ in range(8)]
def foo(a,b):
result = []
while a or b:
if not a:
result.extend(b)
break
if not b:
result.extend(a)
break
x = a.pop(0) if a[0]<b[0] else b.pop(0)
result.append(x)
return result
class PeekIterator(object):
def __init__(self, iterable, sentinel=None):
self.iterator = iter(iterable)
self.sentinel = sentinel
self.next_val = self.sentinel
def peek(self):
if self.next_val is self.sentinel:
try:
self.next_val = next(self.iterator)
except StopIteration:
pass # the sentinel will be returned if there's nothing left in the iterator
return self.next_val
def __iter__(self):
return self
def __next__(self):
if self.next_val is self.sentinel:
return next(self.iterator) # StopIteration is deliberately allowed to bubble out!
val = self.next_val
self.next_val = self.sentinel
return val
next = __next__ # Python 2 compatibility
a_peek = PeekIterator(a)
b_peek = PeekIterator(b)
merged = [next(a_peek) if a_peek.peek() is not None and
(b_peek.peek() is None or a_peek.peek() <= b_peek.peek())
else next(b_peek)
for _ in range(len(a) + len(b))]
upper_bound = float("inf") # any value that compares larger than all the values in both of your lists
peek_a = PeekIterator(a, upper_bound)
peek_b = PeekIterator(b, upper_bound)
merged = [next(peek_a) if peek_a.peek() <= peek_b.peek() else next(peek_b)
for _ in range(len(a)+len(b))]
def merge(left, right):
merge.lhs=0
merge.rhs = 0
def combine(left_side):
if left_side:
el = left[merge.lhs]
merge.lhs += 1
else:
el = right[merge.rhs]
merge.rhs += 1
return el
result = [combine(True) if merge.rhs >= len(right) or (merge.lhs < len(left) and left[merge.lhs] < right[merge.rhs]) else combine(False) for i in range(len(left) + len(right))]
return result
l = [1,3,5]
r = [2,4]
merge(l, r)