给定两个列表(不一定排序),找到这些列表的交集的最有效的非递归算法是什么?
答案 0 :(得分:34)
您可以将第一个列表的所有元素放入哈希集中。然后,迭代第二个,并为其每个元素检查哈希,看它是否存在于第一个列表中。如果是,则将其输出为交集的元素。
答案 1 :(得分:21)
您可能想看一下Bloom过滤器。它们是位向量,它给出概率回答元素是否是集合的成员。可以使用简单的按位AND运算来实现集合交集。如果您有大量的空交叉点,Bloom过滤器可以帮助您快速消除这些交叉点。但是,您仍然需要使用此处提到的其他算法来计算实际交点。 http://en.wikipedia.org/wiki/Bloom_filter
答案 2 :(得分:9)
没有哈希,我想你有两个选择:
答案 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))
其中n
和m
是列表的大小。
编辑:快速排序是递归的,如评论中所述,但看起来有non-recursive implementations
答案 8 :(得分:1)
为什么不实现自己的简单哈希表或哈希集?如果你的名单很大,那么值得避免nlogn交叉。
由于您事先对数据有所了解,因此您应该能够选择一个好的哈希函数。
答案 9 :(得分:1)
如果支持sets(因为你在标题中称它们为内置),通常会有一个交集方法。
无论如何,正如有人说你可以轻松地做到这一点(我不会发布代码,有人已经这样做了)如果您已经对列表进行了排序。如果你不能使用递归就没有问题。有quick sort recursion-less个实现。
答案 10 :(得分:1)
我是第二个“集合”的想法。在JavaScript中,您可以使用第一个列表来填充对象,使用列表元素作为名称。然后使用第二个列表中的列表元素,看看这些属性是否存在。
答案 11 :(得分:1)
使用skip pointers和SSE 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;
}
谢谢。
我对交点的解释是找到交点。
例如:
对于给定的列表 A 和 B,A 和 B 将在点 c1
处“相遇/相交”,并且上面的算法将返回 c1
。由于 OP 声明 OP 无法访问 Hashmaps
或某种类型,我相信 OP 是说算法应该具有 O(1)
空间复杂度。
我前段时间从 Leetcode 得到了这个想法,如果有兴趣:Intersection of Two Linked Lists。