找到子列表之间的交集
Find the intersection between sublists
最近我遇到一个question关于找到子列表之间的交集。告诉子列表有任何(1 个或多个)交集在一起成为一个。例如以下列表:
l=[[1,2,3],[0,13,6],[9,10],[3,4,5],[10,11],[6,7,50]]
必须转换为:
[[1, 2, 3, 4, 5],[0, 50, 6, 7, 13],[9, 10, 11]]
所以我编写了以下函数来完成它,它运行良好且性能良好,我使用 set
来检查成员资格的快速复杂性,并且在内部循环中我使用切片来比较第一个索引主列表与每个循环中的其他元素,还要注意列表将在每个循环后减少,因为它是循环内的递归。 :
s=[set(i) for i in g if i]
def find_intersection(m_list):
for i,v in enumerate(m_list) :
for j,k in enumerate(m_list[i+1:],i+1):
if v & k:
s[i]=v.union(m_list.pop(j))
return find_intersection(m_list)
return m_list
s=[set(i) for i in l if i]
print find_intersection(s)
[set([1, 2, 3, 4, 5]), set([0, 50, 6, 7, 13]), set([9, 10, 11])]
但我认为可以使用另一个性能更好的解决方案来完成,我考虑过 collections.deque
或者 numpy
或者只是修改我的函数并使其更好? .如果您有任何建议,我将不胜感激!
这里有一个更高效的算法:
对于至少一个子列表中存在的每个唯一数字,让我们维护包含该数字的所有子列表的索引列表。如果我们使用排序来查找唯一数字,这部分是 O(n * log n)
时间;如果我们使用散列 table,这部分是 O(n)
时间,其中 n
是所有子列表中元素的总数。
让我们创建一个图,其中顶点是子列表索引,如果两个索引一起出现在所有数字中的至少一个索引列表中,则存在一条边。我们最多需要创建 O(n)
条边(这部分有点重要:不需要显式地创建所有边,我们可以只为所有唯一元素的每个子列表中的一个元素添加一条边到下一个元素由于传递性)。这是一些伪代码:
g = empty graph
for elem in unique_elements:
sublist_indices = list of indices of all sublists that contain this element
for i = 1 ... size(sublist_indices - 1):
g.add_edge(sublist_indices[i], sublist_indices[i + 1])
现在我们可以在线性时间内使用深度优先搜索找到这个图中的连通分量(这个图是无向的)。
我们知道应该合并哪些子列表(当且仅当它们在同一个连接组件中时才应该合并),所以我们可以很容易地构造答案。
总时间复杂度为O(n)
。这是最优的,因为读取输入已经需要 O(n)
操作。
l=[[1,2,3],[0,13,6],[9,10],[3,4,5],[10,11],[6,7,50]]
temp = []
result = []
for i in range(len(l)):
for j in range(i + 1, len(l)):
if set(l[i]).intersection(l[j]):
temp.append(l[i] + l[j])
result.append(list(set(temp[i])))
print result
最近我遇到一个question关于找到子列表之间的交集。告诉子列表有任何(1 个或多个)交集在一起成为一个。例如以下列表:
l=[[1,2,3],[0,13,6],[9,10],[3,4,5],[10,11],[6,7,50]]
必须转换为:
[[1, 2, 3, 4, 5],[0, 50, 6, 7, 13],[9, 10, 11]]
所以我编写了以下函数来完成它,它运行良好且性能良好,我使用 set
来检查成员资格的快速复杂性,并且在内部循环中我使用切片来比较第一个索引主列表与每个循环中的其他元素,还要注意列表将在每个循环后减少,因为它是循环内的递归。 :
s=[set(i) for i in g if i]
def find_intersection(m_list):
for i,v in enumerate(m_list) :
for j,k in enumerate(m_list[i+1:],i+1):
if v & k:
s[i]=v.union(m_list.pop(j))
return find_intersection(m_list)
return m_list
s=[set(i) for i in l if i]
print find_intersection(s)
[set([1, 2, 3, 4, 5]), set([0, 50, 6, 7, 13]), set([9, 10, 11])]
但我认为可以使用另一个性能更好的解决方案来完成,我考虑过 collections.deque
或者 numpy
或者只是修改我的函数并使其更好? .如果您有任何建议,我将不胜感激!
这里有一个更高效的算法:
对于至少一个子列表中存在的每个唯一数字,让我们维护包含该数字的所有子列表的索引列表。如果我们使用排序来查找唯一数字,这部分是
O(n * log n)
时间;如果我们使用散列 table,这部分是O(n)
时间,其中n
是所有子列表中元素的总数。让我们创建一个图,其中顶点是子列表索引,如果两个索引一起出现在所有数字中的至少一个索引列表中,则存在一条边。我们最多需要创建
O(n)
条边(这部分有点重要:不需要显式地创建所有边,我们可以只为所有唯一元素的每个子列表中的一个元素添加一条边到下一个元素由于传递性)。这是一些伪代码:g = empty graph for elem in unique_elements: sublist_indices = list of indices of all sublists that contain this element for i = 1 ... size(sublist_indices - 1): g.add_edge(sublist_indices[i], sublist_indices[i + 1])
现在我们可以在线性时间内使用深度优先搜索找到这个图中的连通分量(这个图是无向的)。
我们知道应该合并哪些子列表(当且仅当它们在同一个连接组件中时才应该合并),所以我们可以很容易地构造答案。
总时间复杂度为O(n)
。这是最优的,因为读取输入已经需要 O(n)
操作。
l=[[1,2,3],[0,13,6],[9,10],[3,4,5],[10,11],[6,7,50]]
temp = []
result = []
for i in range(len(l)):
for j in range(i + 1, len(l)):
if set(l[i]).intersection(l[j]):
temp.append(l[i] + l[j])
result.append(list(set(temp[i])))
print result