高效的列表交集算法

时间:2009-01-30 21:35:40

标签: algorithm list set-intersection

给定两个列表(不一定排序),找到这些列表的交集的最有效的非递归算法是什么?

16 个答案:

答案 0 :(得分:34)

您可以将第一个列表的所有元素放入哈希集中。然后,迭代第二个,并为其每个元素检查哈希,看它是否存在于第一个列表中。如果是,则将其输出为交集的元素。

答案 1 :(得分:21)

您可能想看一下Bloom过滤器。它们是位向量,它给出概率回答元素是否是集合的成员。可以使用简单的按位AND运算来实现集合交集。如果您有大量的空交叉点,Bloom过滤器可以帮助您快速消除这些交叉点。但是,您仍然需要使用此处提到的其他算法来计算实际交点。 http://en.wikipedia.org/wiki/Bloom_filter

答案 2 :(得分:9)

没有哈希,我想你有两个选择:

  • 天真的方式是将每个元素与每个其他元素进行比较。为O(n ^ 2)
  • 另一种方法是首先对列表进行排序,然后迭代它们:O(n lg n)* 2 + 2 * O(n)

答案 3 :(得分:7)

eviews features list来看,它似乎支持复杂的合并和连接(如果这是'连接',就像DB术语一样,它将计算一个交集)。现在挖掘你的文档: - )

此外,eviews有自己的用户论坛 - 为什么不在那里问_

答案 4 :(得分:6)

在C ++中,可以使用STL map

尝试以下内容
vector<int> set_intersection(vector<int> s1, vector<int> s2){

    vector<int> ret;
    map<int, bool> store;
    for(int i=0; i < s1.size(); i++){

        store[s1[i]] = true;
    }
    for(int i=0; i < s2.size(); i++){

        if(store[s2[i]] == true) ret.push_back(s2[i]);

    }
    return ret;
}

答案 5 :(得分:6)

使用第1组构建一个包含O(log n)的二叉搜索树并迭代set2并搜索BST m X O(log n)所以总O(log n) + O(m)+O(log n) ==> O(log n)(m+1)

答案 6 :(得分:3)

这是我提出的另一种可能的解决方案,它具有时间复杂度的O(nlogn)并且没有任何额外的存储空间。你可以在这里查看https://gist.github.com/4455373

以下是它的工作原理:假设这些集合不包含任何重复,请将所有集合合并为一个并对其进行排序。然后遍历合并集并在每次迭代时创建当前索引i和i + n之间的子集,其中n是Universe中可用的集合数。我们循环时所寻找的是一个大小为n的重复序列,它等于宇宙中的集合数。

如果i处的子集等于n处的该子集,则这意味着i处的元素重复n次,其等于集合的总数。并且由于任何集合中都没有重复,这意味着每个集合都包含该值,因此我们将其添加到交集。然后我们将索引移动i + whats保持在它和n之间,因为这些索引肯定不会形成重复序列。

答案 7 :(得分:2)

首先,使用快速排序对两个列表进行排序:O(n * log(n)。然后,首先通过浏览最低值来比较列表,然后添加常用值。例如,在lua中):

function findIntersection(l1, l2)
    i, j = 1,1
    intersect = {}

    while i < #l1 and j < #l2 do
        if l1[i] == l2[i] then
            i, j = i + 1, j + 1
            table.insert(intersect, l1[i])
        else if l1[i] > l2[j] then
            l1, l2 = l2, l1
            i, j = j, i
        else
            i = i + 1
        end
    end

    return intersect
end

O(max(n, m))其中nm是列表的大小。

编辑:快速排序是递归的,如评论中所述,但看起来有non-recursive implementations

答案 8 :(得分:1)

为什么不实现自己的简单哈希表或哈希集?如果你的名单很大,那么值得避免nlogn交叉。

由于您事先对数据有所了解,因此您应该能够选择一个好的哈希函数。

答案 9 :(得分:1)

如果支持sets(因为你在标题中称它们为内置),通常会有一个交集方法。

无论如何,正如有人说你可以轻松地做到这一点(我不会发布代码,有人已经这样做了)如果您已经对列表进行了排序。如果你不能使用递归就没有问题。有quick sort recursion-less个实现。

答案 10 :(得分:1)

我是第二个“集合”的想法。在JavaScript中,您可以使用第一个列表来填充对象,使用列表元素作为名称。然后使用第二个列表中的列表元素,看看这些属性是否存在。

答案 11 :(得分:1)

使用skip pointersSSE instructions可以提高列表交集效率。

答案 12 :(得分:0)

我从this得到了一些你可以申请的好答案。我还没有机会尝试它们,但由于它们也涵盖了交叉路口,你可能会觉得它们很有用。

答案 13 :(得分:0)

在PHP中,类似

function intersect($X) { // X is an array of arrays; returns intersection of all the arrays
  $counts = Array(); $result = Array();
  foreach ($X AS $x) {
    foreach ($x AS $y) { $counts[$y]++; }
  }
  foreach ($counts AS $x => $count) {
    if ($count == count($X)) { $result[] = $x; }
  }
  return $result;
}

答案 14 :(得分:0)

从Big-Oh表示法的定义:

  如果存在正常数c和n 0,则T(N)= O(f(N))   当N≥n0时,T(N)≤cf(N)。

在实践中,这意味着如果两个列表的大小相对较小,那么每两个for循环中少于100个元素就可以了。循环第一个列表并在第二个列表中查找类似对象。 在我的情况下,它工作得很好,因为我的列表中不会有超过10 - 20个最大元素。 然而,一个好的解决方案是排序第一个O(n log n),排序第二个也是O(n log n)并合并它们,另一个O(n log n)粗略地说O(3 n log n),比如说这两个列表大小相同。

答案 15 :(得分:0)

时间:O(n) 空间:O(1) 识别交点的解决方案。

例如,这两个给定的节点将在每次到达终点时通过交换指针来检测交点。 Video Explanation Here.

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    ListNode pA = headA;
    ListNode pB = headB;
    while (pA != pB) {
        pA = pA == null ? headB : pA.next;
        pB = pB == null ? headA : pB.next;
    }
    return pA;
}

谢谢。

编辑

我对交点的解释是找到交点

例如:

Intersection

对于给定的列表 A 和 B,A 和 B 将在点 c1 处“相遇/相交”,并且上面的算法将返回 c1。由于 OP 声明 OP 无法访问 Hashmaps 或某种类型,我相信 OP 是说算法应该具有 O(1) 空间复杂度。

我前段时间从 Leetcode 得到了这个想法,如果有兴趣:Intersection of Two Linked Lists