插入字符串以设置c ++ STL容器的时间复杂度是多少? 据我说,它应该是O(xlogn),其中x是要插入的字符串的长度,n是set的大小。同样,要设置的字符串复制在字符串长度上应该是线性的。 但是我的这段代码可以立即运行。
#include<bits/stdc++.h>
using namespace std;
int main(){
set<string> c;
string s(100000,'a');
for(int i=0;i<100000;i++){
c.insert(s);
}
}
我在哪里错了,难道难道不是10 ^ 10的数量级吗?
答案 0 :(得分:0)
将单个元素插入std::set
的复杂度为O(log n)
,因为它是基于红黑树(https://en.wikipedia.org/wiki/Red%E2%80%93black_tree)。
所以是的,您可以假设它花费O(x * log n)
,其中x
是字符串的长度。
当您尝试插入n
个不同的元素时,算法的复杂度将为O(n * log n)
。
您的情况:
您试图一次又一次地插入相同的字符串,但是std::set
只能包含唯一的元素,因此在您的示例中,您的集合将仅包含1个字符串。
这种方式在每次迭代中(当然,在第一次迭代之后)std::set
仅进行一次比较,并决定不放置相同的字符串。
这就是为什么您的代码运行如此之快的原因。
PS在这种情况下,编译器也可以进行一些优化。
答案 1 :(得分:0)
您应该以某种方式使用 set
,以减少优化循环的风险,例如通过添加return c.size();
。
此外,您对迭代次数的选择可能太低。在循环计数器中添加一个数字,您将看到明显的运行时间。
现代CPU可以轻松处理> 2 * 10 9 ops / s。假设您的编译器使用memcmp
,它可能是手工矢量化的,并且具有一个很小的工作集(例如您的工作集),则您完全是从缓存中进行工作的,并且每次比较(使用AVX2)最多可以达到512字节的吞吐量。假设每次迭代的速率为10个周期,我们仍然可以比较> 10 10 字节/秒。因此,您的程序应在<1 s的中等硬件上运行。
请尝试使用此更新的代码:
#include <string>
#include <set>
using namespace std;
int main(){
set<string> c;
string s(100000,'a');
for(int i=0;i<1000000;i++) { // Add a digit here
c.insert(s);
}
return c.size(); // use something from the set
}
在(-O3
)上启用优化后,这需要大约5秒钟才能在我的系统上运行。
换句话说,是的,插入二叉树的复杂度为O(log n),但是比较字符串的复杂度为O(n)。这些n不相同,在map
的情况下,它表示映射的大小;在string
的情况下,n表示字符串的长度。
在您的特定情况下,地图只有一个元素,因此插入值为O(1)。您纯粹从字符串比较中获得线性复杂度O(n),其中 n 是 string_length * number_of_iterations 。