我有两套(或地图),需要有效地处理他们的交集。 我知道有两种方法可以做到这一点:
根据尺寸的不同,这两种解决方案中的任何一种都明显更好(定时),因此我需要根据尺寸(这有点凌乱)在这些算法之间切换 - 或者找到一个优于两者的解决方案,例如使用map.find()的一些变体将前一个迭代器作为提示(类似于map.emplace_hint(...)) - 但我找不到这样的函数。
问题:是否可以直接使用STL或某些兼容的库来结合两种解决方案的性能特征?
请注意,性能要求使其与早期的问题不同,例如 Efficient intersection of sets?
答案 0 :(得分:4)
几乎在每种情况下std::set_intersection
都是最佳选择。
只有当集合包含非常少量的元素时,其他解决方案才可能更好。
由于原木的性质与基地二。
其缩放为:
n = 2,log(n)= 1
n = 4,log(n)= 2
n = 8,log(n)= 3
.....
n = 1024 log(n)= 10
O(如果集合的长度超过5-10个元素,则n1 * log(n2)比O(n1 + n2)复杂得多。
这样的功能被添加到STL是有原因的,并且它是这样实现的。它还将使代码更具可读性。
对于长度小于20但很少使用的集合,选择排序比合并或快速排序要快。
答案 1 :(得分:2)
对于作为二叉树实现的集合,实际上是一种算法,它结合了您提到的两个过程的好处。本质上,你像std :: set_intersection那样进行合并,但是在一棵树中迭代时,你跳过任何小于另一棵树当前值的分支。
得到的交点需要 O(min(n1 log n2,n2 log n1,n1 + n2),这正是你想要的。
不幸的是,我非常确定std :: set不提供可以支持此操作的接口。
过去我曾经做过几次,当时我正在加入倒排索引和类似的东西。通常我使用skipTo(x)操作生成迭代器,该操作将前进到下一个元素> = x。为了满足我承诺的复杂性,它必须能够在log(N)摊销时间内跳过N个元素。然后一个十字路口看起来像这样:
void get_intersection(vector<T> *dest, const set<T> set1, const set<T> set2)
{
auto end1 = set1.end();
auto end2 = set2.end();
auto it1 = set1.begin();
if (it1 == end1)
return;
auto it2 = set2.begin();
if (it2 == end2)
return;
for (;;)
{
it1.skipTo(*it2);
if (it1 == end1)
break;
if (*it1 == *it2)
{
dest->push_back(*it1);
++it1;
}
it2.skipTo(*it1);
if (it2 == end2)
break;
if (*it2 == *it1)
{
dest->push_back(*it2);
++it2;
}
}
}
使用迭代器向量可以很容易地扩展到任意数量的集合,并且几乎任何有序集合都可以扩展以提供所需的迭代器 - 排序数组,二叉树,b树,跳过列表等。 / p>
答案 2 :(得分:0)
关于性能要求,O(n1 + n2)在大多数情况下是非常好的复杂性,所以只有在紧密循环中进行此计算时才值得考虑。
如果你确实需要它,那么组合方法也不算太糟糕,或许类似于什么?
伪代码:
x' = set_with_min_length([x, y])
y' = set_with_max_length([x, y])
if (x'.length * log(y'.length)) <= (x'.length + y'.length):
return iterate_over_map_find_elements_in_other(y', x')
return std::set_intersection(x, y)
我认为你不会找到能够击败其中任何一种复杂性的算法,但很高兴被证明是错误的。
答案 3 :(得分:0)
我不知道如何使用标准库执行此操作,但如果您编写了自己的平衡二叉搜索树,则以下是如何使用提示&#34;实现有限的&#34;查找。 (根据您的其他要求,BST重新实现也可能遗漏父指针,这可能是对STL的性能胜利。)
假设提示值小于要找到的值,并且我们知道提示节点属于其左子树的提示节点的祖先堆栈。首先通常在提示节点的右子树中搜索,按照保证将节点推送到堆栈(以准备下次提示)。如果这不起作用,那么当堆栈的顶级节点的值小于查询值时,弹出堆栈。从弹出的最后一个节点(如果有的话)中搜索,按照保证推送。
我声称,当使用这种机制连续搜索值按升序排列时,(1)每个树边最多遍历一次,(2)每个查找遍历最多两个下行路径的边。给定具有n2个节点的二叉树中的2 * n1个下行路径,边缘的成本为O(n1 log n2)。它也是O(n2),因为每个边都被遍历了一次。