set的常見問題(轉)
(1)為何map和set的插入刪除效率比用其他序列容器高?
大部分人說,很簡單,因為對於關聯容器來說,不需要做內存拷貝和內存移動。說對了,確實如此。set容器內所有元素都是以節點的方式來存儲,其節點結構和鏈表差不多,指向父節點和子節點。結構圖可能如下:
A
/ \
B C
/ \ / \
D E F G
因此插入的時候只需要稍做變換,把節點的指針指向新的節點就可以了。刪除的時候類似,稍做變換后把指向刪除節點的指針指向其他節點也OK了。這里的一切操作就是指針換來換去,和內存移動沒有關系。
(2)為何每次insert之后,以前保存的iterator不會失效?
iterator這里就相當於指向節點的指針,內存沒有變,指向內存的指針怎么會失效呢(當 然被刪除的那個元素本身已經失效了)。相對於vector來說,每一次刪除和插入,指針都有可能失效,調用push_back在尾部插入也是如此。因為為 了保證內部數據的連續存放,iterator指向的那塊內存在刪除和插入過程中可能已經被其他內存覆蓋或者內存已經被釋放了。即使時push_back的 時候,容器內部空間可能不夠,需要一塊新的更大的內存,只有把以前的內存釋放,申請新的更大的內存,復制已有的數據元素到新的內存,最后把需要插入的元素 放到最后,那么以前的內存指針自然就不可用了。特別時在和find等算法在一起使用的時候,牢記這個原則:不要使用過期的iterator。
(3)當數據元素增多時,set的插入和搜索速度變化如何?
如果你知道log2的關系你應該就徹底了解這個答案。在set中查找是使用二分查找,也就是 說,如果有16個元素,最多需要比較4次就能找到結果,有32個元素,最多比較5次。那么有10000個呢?最多比較的次數為log10000,最多為 14次,如果是20000個元素呢?最多不過15次。看見了吧,當數據量增大一倍的時候,搜索次數只不過多了1次,多了1/14的搜索時間而已。你明白這 個道理后,就可以安心往里面放入元素了。
2 #include<vector> 3 #include<set> 4 #include<stdio.h> 5 #include<sys/time.h> 6 7 const int max_num = 10000; 8 9 int main(int argc, char** argv){ 10 std::vector<int> vec; 11 std::set<int> se; 12 struct timeval begin; 13 struct timeval end; 14 gettimeofday(&begin, NULL); 15 for(int i=0;i<max_num;i++) 16 vec.push_back(i); 17 gettimeofday(&end, NULL); 18 long time_vec=end.tv_usec - begin.tv_usec; 19 gettimeofday(&begin, NULL); 20 for(int i=0;i<max_num;i++) 21 se.insert(i); 22 gettimeofday(&end, NULL); 23 long time_set=end.tv_usec - begin.tv_usec; 24 printf("vec: %ld\n", time_vec); 25 printf("set: %ld\n", time_set); 26 // 刪除批量數據 27 gettimeofday(&begin, NULL); 28 vec.erase(vec.begin(),vec.begin()+500); 29 gettimeofday(&end, NULL); 30 time_vec=end.tv_usec - begin.tv_usec; 31 std::set<int>::iterator it; 32 int i=0; 33 for(it=se.begin();i<500;i++,it++) 34 35 gettimeofday(&begin, NULL); 36 se.erase(se.begin(),it); 37 gettimeofday(&end, NULL); 38 time_set=end.tv_usec - begin.tv_usec; 39 printf("vec erase much :%d\n", time_vec); 40 printf("sec erase much :%d\n", time_set); 41 //刪除一個數據 42 gettimeofday(&begin, NULL); 43 vec.erase(vec.begin()); 44 gettimeofday(&end, NULL); 45 time_vec=end.tv_usec - begin.tv_usec; 46 gettimeofday(&begin, NULL); 47 se.erase(se.begin()); 48 gettimeofday(&end, NULL); 49 time_set=end.tv_usec - begin.tv_usec; 50 printf("vec erase single:%d\n", time_vec); 51 printf("sec erase single:%d\n", time_set); 52 53 return 0; 54 }
運行結果:
max_num=10000
vec: 230
set: 6627
vec erase much :4
sec erase much :36
vec erase single:10
sec erase single:0
max_num=999999
vec: 17957
set: 38627
vec erase much :545
sec erase much :41
vec erase single:1058
sec erase single:0
當插入的數據是隨機數時,不是順序產生的序列時
max_num=9999999
vec: 150897
set: 153710
可以看到,兩者插入時間幾乎一致