![](/img/trans.png)
[英]Fastest way to compare ordered lists and count common elements *including* duplicates
[英]Fastest union for multiple sorted lists removing duplicates and get an ordered result
您可以为此使用heapq.merge
:
def mymerge(v):
from heapq import merge
last = None
for a in merge(*v):
if a != last: # remove duplicates
last = a
yield a
print(list(mymerge([[1,3,7,20,31], [1,2,5,6,7], [2,4,25,26]])))
# [1, 2, 3, 4, 5, 6, 7, 20, 25, 26, 31]
(已编辑)
解决该问题的渐近理论最佳方法是使用优先级队列,例如在heapq.merge()
实现的heapq.merge()
(感谢@kaya3 指出这一点)。
然而,在实践中,许多事情可能会出错。 例如,复杂性分析中的常数因素足够大,以至于理论上最佳的方法在现实生活中会变慢。 这从根本上取决于实现。 例如,Python 会因显式循环而遭受一些速度损失。
因此,让我们考虑几种方法以及 do 如何针对某些具体输入执行。
为了让您对我们正在讨论的数字有所了解,这里有一些方法:
merge_sorted()
使用最朴素的方法展平序列,将其缩减为set()
(删除重复项)并根据需要对其进行排序import itertools
def merge_sorted(seqs):
return sorted(set(itertools.chain.from_iterable(seqs)))
merge_heapq()
本质上是@arshajii 的回答。 请注意, itertools.groupby()
变化略快(小于 ~1%)。import heapq
def i_merge_heapq(seqs):
last_item = None
for item in heapq.merge(*seqs):
if item != last_item:
yield item
last_item = item
def merge_heapq(seqs):
return list(i_merge_heapq(seqs))
merge_bisect_set()
基本上与merge_sorted()
算法相同,除了现在使用高效的bisect
模块显式构造结果以进行排序插入。 由于sorted()
基本上是在做同样的事情,但在 Python 中循环,这不会更快。import itertools
import bisect
def merge_bisect_set(seqs):
result = []
for item in set(itertools.chain.from_iterable(seqs)):
bisect.insort(result, item)
return result
merge_bisect_cond()
类似于merge_bisect_set()
但现在非重复约束是使用 final list
明确完成的。 然而,这比仅使用set()
要昂贵得多(实际上它太慢以至于被排除在绘图之外)。def merge_bisect_cond(seqs):
result = []
for item in itertools.chain.from_iterable(seqs):
if item not in result:
bisect.insort(result, item)
return result
merge_pairwise()
明确实现了理论上有效的算法,类似于您在问题中概述的算法。def join_sorted(seq1, seq2):
result = []
i = j = 0
len1, len2 = len(seq1), len(seq2)
while i < len1 and j < len2:
if seq1[i] < seq2[j]:
result.append(seq1[i])
i += 1
elif seq1[i] > seq2[j]:
result.append(seq2[j])
j += 1
else: # seq1[i] == seq2[j]
result.append(seq1[i])
i += 1
j += 1
if i < len1:
result.extend(seq1[i:])
elif j < len2:
result.extend(seq2[j:])
return result
def merge_pairwise(seqs):
result = []
for seq in seqs:
result = join_sorted(result, seq)
return result
merge_loop()
实现了上述的泛化,现在 pass 只对所有序列执行一次,而不是成对执行。def merge_loop(seqs):
result = []
lengths = list(map(len, seqs))
idxs = [0] * len(seqs)
while any(idx < length for idx, length in zip(idxs, lengths)):
item = min(
seq[idx]
for idx, seq, length in zip(idxs, seqs, lengths) if idx < length)
result.append(item)
for i, (idx, seq, length) in enumerate(zip(idxs, seqs, lengths)):
if idx < length and seq[idx] == item:
idxs[i] += 1
return result
通过使用以下方法生成输入:
def gen_input(n, m=100, a=None, b=None):
if a is None and b is None:
b = 2 * n * m
a = -b
return tuple(tuple(sorted(set(random.randint(int(a), int(b)) for _ in range(n)))) for __ in range(m))
可以绘制变化n
的时间:
请注意,一般而言,性能会因n
(每个序列的大小)和m
(序列数量)以及a
和b
(生成的最小和最大数量)的不同值而异。 为简洁起见,本答案中未对此进行探讨,但请在此处随意使用它,其中还包括一些其他实现,尤其是 Cython 的一些试探性加速,但仅部分成功。
您可以在 Python-3 中使用集合。
mylist = [[1,3,7,20,31], [1,2,5,6,7], [2,4,25,26]]
mynewlist = mylist[0] + mylist[1] + mylist[2]
print(list(set(mynewlist)))
输出:
[1, 2, 3, 4, 5, 6, 7, 20, 25, 26, 31]
首先使用列表添加合并所有子列表。
然后将其转换为一个集合对象,它将删除所有重复项,这些重复项也将按升序排序。
将其转换回列表。它提供您想要的输出。
希望它能回答你的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.