显然使用
std::set_intersection()
是浪费时间。
算法头文件中没有一个可以直接实现这个功能的函数吗?
据我理解,std::find_first_of()
执行的是线性搜索。std::set
(或多个)的解决方案。对于map,需要稍微多做一些工作。100
在理论上是错误的。对于最佳Big-O性能,应该是k n lg m > m
而不是100 n > m
,但是常数因子很大,并且100>lg m,所以你应该进行实验。set_intersection
一样。我们使用.lower_bound
而不是仅使用++
来尝试更快地跳过每个列表。{1,3,7}
和{0,2,4,6,8}
),则这将比仅使用++慢一个对数因子。++
替换lower_bound
部分。template<class Lhs, class Rhs>
bool sorted_has_overlap( Lhs const& lhs, Rhs const& rhs ) {
if (lhs.empty() || rhs.empty()) return false;
if (lhs.size() * 100 < rhs.size()) {
for (auto&& x:lhs)
if (rhs.find(x)!=rhs.end())
return true;
return false;
}
if (rhs.size() * 100 < lhs.size()) {
for(auto&& x:rhs)
if (lhs.find(x)!=lhs.end())
return true;
return false;
}
using std::begin; using std::end;
auto lit = begin(lhs);
auto lend = end(lhs);
auto rit = begin(rhs);
auto rend = end(rhs);
while( lit != lend && rit != rend ) {
if (*lit < *rit) {
lit = lhs.lower_bound(*rit);
continue;
}
if (*rit < *lit) {
rit = rhs.lower_bound(*lit);
continue;
}
return true;
}
return false;
}
std::lower_bound
来快速推进“其他”容器。这样做的优点是使用部分搜索(在set
中无法快速执行)。与天真的++
相比,它也会在“交错”元素上表现不佳(以对数n的因素)。std
中的算法调用。这样的转换基本上是机械的。++
差--下一个元素是符号交换的情况需要O(1)操作,并且如果间隔较大,则将O(k)替换为O(lg k)。std::lower_bound
是最优的(在随机访问迭代器上)。使用一个输出迭代器,如果写入则抛出异常。当您捕获该异常时返回true,否则返回false。std::set_intersection
合法。)
auto
:http://en.cppreference.com/w/cpp/language/range-for (我认为模仿这个是最佳实践) - Yakk - Adam Nevraumontconst auto&
пјҢд»ҘйҳІLhs::value_typeеӨҚеҲ¶ејҖй”ҖиҫғеӨ§пјҲжҲ–дёҚеҸҜеӨҚеҲ¶дҪҶеҸҜ移еҠЁпјүгҖӮ - Richard Hodgesfor(auto&& x:blah)
就是正确的做法,所以我默认使用它而不需要思考。而且,const
比 &
多了很多字符!所有剩余的非引用 auto
都是迭代器的副本,如果你有昂贵的迭代器需要复制,那么你就错了。 ;) - Yakk - Adam Nevraumont<
不是 <=
。如果 m
的增长速度严格小于 n/lgn
的增长速度,则更详细地说,m lg n
在渐近意义下比 m+n
更快。 - Yakk - Adam NevraumontO(f(m))
是O(n/lgn)
的一个严格子集,其中O(f(x))
被用作符号表示一些固定的k
使得函数渐近地受到k f(x)
的限制。但是<
在这个上下文中更容易理解,并且在惯例上也有相同的含义。 - Yakk - Adam Nevraumont如果输入已排序,您可以使用以下模板函数:
template<class InputIt1, class InputIt2>
bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
{
while (first1 != last1 && first2 != last2) {
if (*first1 < *first2) {
++first1;
continue;
}
if (*first2 < *first1) {
++first2;
continue;
}
return true;
}
return false;
}
#include <iostream>
int main() {
int a[] = {1, 2, 3};
int b[] = {3, 4};
int c[] = {4};
std::cout << intersect(a, a + 3, b, b + 2) << std::endl;
std::cout << intersect(b, b + 2, c, c + 1) << std::endl;
std::cout << intersect(a, a + 3, c, c + 1) << std::endl;
}
结果:
1
1
0
这个函数的复杂度为 O(n + m)
,其中 n
、m
是输入大小。但是如果一个输入比另一个输入小得多(例如 n << m),最好使用二分查找检查每个 n
元素是否属于另一个输入。这将花费 O(n * log(m))
的时间。
#include <algorithm>
template<class InputIt1, class InputIt2>
/**
* When input1 is much smaller that input2
*/
bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2) {
while (first1 != last1)
if (std::binary_search(first2, last2, *first1++))
return true;
return false;
}
+inf
,另一个包含 200 万个双精度浮点数(全部小于 +inf
)。您的代码执行 200 万次递增操作。如果第一个容器包含一堆负数,然后是 +inf
,第二个容器包含一堆正数,则您将遍历两个集合中的 每个 元素。最好情况下它执行 min(n,m)
次操作,但最坏情况下它执行 n+m
次操作。 - Yakk - Adam Nevraumont{1, 2, 10}, {3, 4, 5, 6, 7, 8, 9}
。 - T.C.O(n+m)
都比 O(n*log(m))
小。 - Bill Lynchn
= 1,m
= 十亿。O(n+m)= 十亿零一。O(n lg m)=30。那么,当1000000001再次小于30时,确切的时间是什么? - Yakk - Adam NevraumontO(n+m)
支配了O(n lg m)
。对于任何固定的 n,随着 m 的增长,O(n+m)
都支配了O(n lg m)
。只有当 n 和 m 都增长时,O(n+m)
才会被O(n lg m)
和O(m lg n)
所支配。多变量复杂性分析的重点在于有时某些值不会增长,而其他常数会增长,有时增长率(变量之间)不是线性的。如果强制所有变量线性增长,则多变量复杂性没有意义。 - Yakk - Adam Nevraumont{0,2,3,6,7}
编码在内存字中:...00000011001101
。规则是:如果且仅当数字i
在集合中时,从右向左读取的第i
位为高位。a
和b
中,你可以使用按位运算符&
执行交集操作。int a = ...;
int b = ...;
int intersection = a & b;
int union = a | b; // bonus
我觉得你可以做一个二分查找
#include <set>
#include <iostream>
#include <algorithm>
bool overlap(const std::set<int>& s1, const std::set<int>& s2)
{
for( const auto& i : s1) {
if(std::binary_search(s2.begin(), s2.end(), i))
return true;
}
return false;
}
int main()
{
std::set<int> s1 {1, 2, 3};
std::set<int> s2 {3, 4, 5, 6};
std::cout << overlap(s1, s2) << '\n';
}
std::set
迭代器是双向迭代器,不是随机访问迭代器,所以使用std::binary_search
没有任何意义。只需要使用s2.count(i)
即可。 - roman-kashitsyn
std::set_intersection()
是线性的,任何对这个问题的快速解决方案也将是线性的。 - QuestionC