Python 找到子列表之间的交集

Python 找到子列表之间的交集,python,list,numpy,data-structures,intersection,Python,List,Numpy,Data Structures,Intersection,最近我遇到一个关于查找子列表之间的交集的问题。这告诉子列表,其中有任何(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检查成员身份的快速复杂性,在内部循环中,我使用切片将主列表的第一个索引与每个循环中的

最近我遇到一个关于查找子列表之间的交集的问题。这告诉子列表,其中有任何(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)
    时间;如果我们使用哈希表,则此部分为
    O(n)
    ,其中
    n
    是所有子列表中的元素总数

  • 让我们创建一个图,其中顶点是子列表索引,如果两个索引一起出现在所有数字中的至少一个索引列表中,则存在一条边。我们最多需要创建
    O(n)
    edges(这一部分非常重要:不需要显式地创建所有边,我们只需在每个子列表中为所有唯一元素的下一个元素添加一条边即可)。下面是一些伪代码:

    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)
    操作。

    这还不错,但我认为您没有很好地理解我的代码,我使用集合来检查成员身份,因为它的
    O(1)
    ,而我使用2来检查它的非
    O(n^2)
    可能是因为内部列表的长度以及主列表在任何循环后都会减少。@Kasra您的代码至少是
    O(n^2)
    (可能更多)。例如,如果所有子列表只包含一个元素,并且只有最后两个元素相交,那么将进行n*(n-1)/2次迭代。有一点。@Kasra试试这个测试用例:[[1],[2],…,[n-2],[n],[n]]。毫无疑问,您的解决方案会对它执行
    O(n^2)
    操作。加上1。我必须说,我知道这一点,但我没有理解。所以这是最坏的情况!由于您的解决方案是一个很好的解决方案算法,我需要等待pythonic的建议。如果您能解释代码适合解决OP问题的内容和原因,我会很有帮助。
    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