我一直在尝试在python中写下一个列表交集算法来处理重复。我是python和编程的新手,所以请原谅我,如果这听起来效率低下,但我无法想出其他任何东西。这里,L1和L2是有问题的两个列表,L是交集。
我100%确定这不是Mathematica中用于评估列表交集的算法,但我无法真正提出任何更有效的方法。我不想在这个过程中修改L1和L2,因此我将两个列表添加回交集。有任何想法吗?我不想使用除列表之外的任何内置函数/数据类型,因此没有导入集或类似的东西。就我而言,这是一个算法和实现练习,而不是编程练习。
答案 0 :(得分:1)
怎么样:
效率不是很高,但在代码中它看起来像这样(重复说明一点):
>>> L1 = [1,2,3,3,4]
>>> L2 = [2,3,4,4,5]
>>> L = list()
>>> for v1 in L1:
for v2 in L2:
if v1 == v2 and v1 not in L:
L.append(v1)
>>> L
[2,3,4]
您可以通过检查元素是否已经在L中来避免从L1和L2中删除,如果不是则添加到L。那么L1和L2中是否有重复并不重要。
答案 1 :(得分:1)
编辑:我读错了标题,并浏览了内置部分。无论如何我会留在这里,可能会帮助别人。
您可以使用set
类型实现此目的。
>>> a = [1,2,3,4]
>>> b = [3,4,5,6]
>>> c = list(set(a) & set(b))
>>> c
[3, 4]
答案 2 :(得分:1)
任何遍历L1
的内容,每次迭代所有L2
,都会花费二次时间。改进它的唯一方法是避免遍历所有L2
。 (最后从L
删除重复项也存在类似的问题。)
如果您使用set
L2
(和L
),当然每个in L2
步都是固定时间,因此整体算法是线性的。而且您始终可以构建自己的哈希表实现,而不是使用set
。但这是很多工作。
使用二叉搜索树,甚至只是排序列表和binary_find
函数,您可以在O(N log N)中执行此操作。并且binary_find
更容易自己写。所以:
S2 = sorted(L2)
L = [element for element in L1 if binary_find(element, S2)]
S = remove_adjacent(sorted(L))
或者,更简单地说,排序L1,然后你不需要remove_adjacent
:
S1, S2 = sorted(L1), sorted(L2)
L = []
for element in S1:
if binary_find(element, S2) and (not L or L[-1] != element):
L.append(element)
无论哪种方式,这都是O(N log N),其中N是较长列表的长度。相比之下,原始是O(N ^ 2),其他答案是O(N ^ 3)。当然它有点复杂,但它仍然很容易理解。
你需要编写binary_find
(如果适用,remove_adjacent
),因为我假设你不想使用stdlib中的东西,如果你甚至不想使用额外的内置。但那真的很容易。例如:
def binary_find(element, seq):
low, high = 0, len(seq),
while low != high:
mid = (low + high) // 2
if seq[mid] == element:
return True
elif seq[mid] < element:
low = mid+1
else:
high = mid
return False
def remove_adjacent(seq):
ret = []
last = object()
for element in seq:
if element != last:
ret.append(element)
last = element
return ret
如果您甚至不想使用sorted
或list.sort
,也可以轻松编写自己的排序。
答案 3 :(得分:1)
这是一个更快的解决方案:
def intersect_sorted(a1, a2):
"""Yields the intersection of sorted lists a1 and a2, without deduplication.
Execution time is O(min(lo + hi, lo * log(hi))), where lo == min(len(a1),
len(a2)) and hi == max(len(a1), len(a2)). It can be faster depending on
the data.
"""
import bisect, math
s1, s2 = len(a1), len(a2)
i1 = i2 = 0
if s1 and s1 + s2 > min(s1, s2) * math.log(max(s1, s2)) * 1.4426950408889634:
bi = bisect.bisect_left
while i1 < s1 and i2 < s2:
v1, v2 = a1[i1], a2[i2]
if v1 == v2:
yield v1
i1 += 1
i2 += 1
elif v1 < v2:
i1 = bi(a1, v2, i1)
else:
i2 = bi(a2, v1, i2)
else: # The linear solution is faster.
while i1 < s1 and i2 < s2:
v1, v2 = a1[i1], a2[i2]
if v1 == v2:
yield v1
i1 += 1
i2 += 1
elif v1 < v2:
i1 += 1
else:
i2 += 1
它在O(min(n + m, n * log(m)))
时间运行,其中n是长度的最小值,m是最大值。它同时迭代两个列表,尽可能跳过尽可能多的元素。
此处提供分析:http://ptspts.blogspot.ch/2015/11/how-to-compute-intersection-of-two.html
答案 4 :(得分:0)
if element in list2
)中,并且还不在您的临时列表中(语法相同)我觉得发布解决方案很糟糕,但它比我的文字更可读:
def intersection(l1, l2):
temp = []
for item in l1:
if item in l2 and item not in temp:
temp.append(item)
return temp
答案 5 :(得分:0)
计算保留订单和消除重复项的两个列表的交集的pythonic且有效的方法如下:
L1 = [1,2,3,3,4,4,4,5,6]
L2 = [2,4,6]
aux = set()
L = [x for x in L1 if x in L2 and not (x in aux or aux.add(x)) ]
解决方案使用set&#34; aux&#34;存储已添加到结果列表中的元素。
请注意,您不需要&#34;导入&#34;集,因为它们是Python中的本机数据类型。但是,如果您坚持不使用集合,则可以选择使用列表的效率较低的版本:
L1 = [1,2,3,3,4,4,4,5,6]
L2 = [2,4,6]
aux = []
L = [x for x in L1 if x in L2 and not (x in aux or aux.append(x)) ]