【轉】c++ set的用法
1、關於set
C++ STL之所以得到廣泛的讚譽,也被很多人使用,不只是提供了像vector,string,list等方便的容器,更重要的是STL封裝了許多複雜的資料結構演算法和大量常用資料結構操作。vector封裝了陣列,list封裝了連結串列,map和set封裝了二叉樹等。在封裝這些資料結構的時候,STL按照程式設計師的使用習慣,以成員函式方式提供的常用操作,如:插入、排序、刪除、查詢等。
關於set,必須說明的是set關聯式容器。set作為一個容器也是用來儲存同一資料型別的資料型別。set中每個元素的值都唯一,而且系統能根據元素的值自動進行排序。C++ STL中標準關聯容器set,multiset,map,multimap內部採用的就是一種非常高效的平衡檢索二叉樹:紅黑樹,也稱為RB樹(Red-Black Tree)。RB樹的統計效能要好於一般平衡二叉樹,所以被STL選擇作為了關聯容器的內部結構。
(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的插入和搜尋速度變化如何?
在set中查詢是使用二分查詢,複雜度是log2n。也就是說,如果有16個元素,最多需要比較4次就能找到結果,有32個元素,最多比較5次。那麼有100000個呢?最多比較的次數為log10000,最多為14次,如果是200000個元素呢?最多不過15次。當資料量增大一倍的時候,搜尋次數只不過多了1次,多了1/14的搜尋時間而已。
2、set中常用的方法
begin() ,返回一個迭代器,返回的值為set容器的第一個元素
end() ,返回一個迭代器,返回的值為set容器的最後一個元素
clear() ,刪除set容器中的所有的元素
empty()
max_size() ,返回set容器可能包含的元素最大個數
size() ,返回當前set容器中的元素個數
rbegin() ,返回一個逆迭代器,返回的值和end()相同
rend() ,返回一個逆迭代器,它指向容器c的第一個元素前面的位置
count() ,用來查詢set中某個某個鍵值出現的次數。這個函式在set並不是很實用,因為一個鍵值在set只可能出現0或1次,這樣就變成了判斷某一鍵值是否在set出現過了。
erase(iterator) ,刪除定位器iterator指向的值
erase(first,second) ,刪除定位器first和second之間的值,包含first指向的值,不包含second指向的值,即左閉右開
erase(key_value) ,刪除鍵值key_value的值
試試erase的用法:
1 #include <iostream> 2 #include <set> 3 4 using namespace std; 5 6 int main(int argc,char* argv[]) 7 { 8 set<int> s; 9 set<int>::iterator iter; 10 set<int>::const_iterator const_iter; 11 set<int>::iterator first; 12 set<int>::iterator second; 13 14 for(int i = 1; i <= 10; ++i) 15 { 16 s.insert(i); 17 cout << i << " "; 18 } 19 cout << endl; 20 iter = s.begin(); 21 cout << "第一次刪除的是:" << *iter << endl; 22 s.erase(s.begin()); 23 cout << "第一次刪除之後:" << endl; 24 for(const_iter = s.begin();const_iter != s.end();++const_iter) 25 { 26 cout << *const_iter << " "; 27 } 28 cout << endl; 29 first = s.begin(); 30 second = s.begin(); 31 second++; 32 second++; 33 cout << "l == " << *first << " r == " << *second << endl; 34 s.erase(first,second); 35 cout << "第二次刪除之後:" << endl; 36 for(const_iter = s.begin();const_iter != s.end();++const_iter) 37 { 38 cout << *const_iter << " "; 39 } 40 cout << endl; 41 42 s.erase(8); 43 cout << "第三次刪除的是:8" << endl; 44 cout << "第三次刪除之後set中元素是:" ; 45 for(const_iter = s.begin();const_iter != s.end();++const_iter) 46 { 47 cout << *const_iter << " "; 48 } 49 cout << endl; 50 return 0; 51 }
執行結果:
find() ,返回給定值的定位器,如果沒找到則返回end()。
1 #include <iostream> 2 #include <set> 3 4 using namespace std; 5 6 int main(int argc,char* argv[]) 7 { 8 int a[] = {4,6,2,3}; 9 set<int> s(a,a + 4); 10 set<int>::iterator iter; 11 for(iter = s.begin();iter != s.end();iter++) 12 { 13 cout << *iter << " "; 14 } 15 16 cout << endl; 17 if((iter = s.find(2)) != s.end()) 18 cout<< "Hit " << *iter << endl; 19 if((iter = s.find(1)) != s.end()) 20 cout << "Hit " << *iter << endl; 21 else 22 cout <<"未找到1" << endl; 23 24 return 0; 25 }
執行結果:
insert(key_value); 將key_value插入到set中,返回值是pair<set<int>::iterator,bool>,bool標誌著插入是否成功,而iterator代表插入的位置,若key_value已經在set中,則iterator表示的key_value在set中的位置。
inset(first,second); 將定位器first到second之間的元素插入到set中,返回值是void.
示例程式碼:
1 #include <iostream> 2 #include <set> 3 4 using namespace std; 5 6 int main() 7 { 8 int a[] = {1, 4, 3, 7}; 9 set<int> s; 10 set<int>::iterator iter; 11 s.insert(a,a + 4); 12 for(iter = s.begin() ; iter != s.end() ; ++iter) 13 { 14 cout<<*iter<<" "; 15 } 16 cout<<endl; 17 pair<set<int>::iterator,bool> pr; 18 pr = s.insert(5); 19 if(pr.second) 20 { 21 cout<<*pr.first<<endl; 22 } 23 cout << " 插入數值之後的set中有:" << endl; 24 for(iter = s.begin() ; iter != s.end() ; ++iter) 25 { 26 cout<<*iter<<" "; 27 } 28 cout<<endl; 29 30 return 0; 31 }
執行結果:
lower_bound(key_value),返回第一個大於等於key_value的定位器
upper_bound(key_value),返回第一個大於key_value的定位器
示例程式碼:
1 #include <iostream> 2 #include <set> 3 4 using namespace std; 5 6 int main(int argc,char* argv[]) 7 { 8 int a[] = {1,3,5,4,6}; 9 set<int> s; 10 s.insert(a,a + 5); 11 set<int>::iterator iter; 12 for(iter = s.begin();iter != s.end();++iter) 13 { 14 cout << *iter << " "; 15 } 16 cout << endl; 17 cout << "第一個大於等於2的值是: "; 18 cout << *s.lower_bound(2) << endl; 19 cout << "第一個大於等於3的值是: "; 20 cout << *s.lower_bound(3) << endl; 21 cout << "第一個大於3的值是: "; 22 cout << *s.upper_bound(3) << endl; 23 return 0; 24 }
執行結果:
3、自定義比較函式
3.1過載<操作符
1 class song 2 { 3 public: 4 song(int id,int hot) 5 { 6 this->m_id = id; 7 this->m_hot = hot; 8 } 9 bool operator<(const class song & right)const //過載<運算子 10 { 11 if(this->m_id == right.m_id) //根據id去重 12 return false; 13 if(this->m_hot != right.m_hot) 14 return this->m_hot > right.m_hot; //按熱度降序 15 else 16 return this->m_id > right.m_id; //熱度相同,按id降序 17 } 18 int m_id; 19 int m_hot; 20 }; 21 int main(int argc,char* argv[]) 22 { 23 set<song> mySet; 24 song s1(10,100); 25 song s2(20,200); 26 song s3(20,300); 27 song s4(30,200); 28 mySet.insert(s1); //插入s1 29 mySet.insert(s2); //插入s2 30 mySet.insert(s3); //s3和s2的id相同,不插入 31 mySet.insert(s4); //插入s4 32 for(auto it : mySet) 33 { 34 cout << "id:" << it.m_id << ",hot:" << it.m_hot << endl; 35 } 36 cout << "end" << endl; 37 return 0; 38 }
執行結果:
3.2過載()運算子
1 #include <iostream> 2 #include <set> 3 4 using namespace std; 5 class song 6 { 7 public: 8 song(int id,int hot) 9 { 10 this->m_id = id; 11 this->m_hot = hot; 12 } 13 /* 14 bool operator<(const class song & right)const //過載<運算子 15 { 16 if(this->m_id == right.m_id) //根據id去重 17 return false; 18 if(this->m_hot != right.m_hot) 19 return this->m_hot > right.m_hot; //按熱度降序 20 else 21 return this->m_id > right.m_id; //熱度相同,按id降序 22 } 23 */ 24 int m_id; 25 int m_hot; 26 }; 27 class comp 28 { 29 public: 30 bool operator()(class song left,class song right) //過載()運算子 31 { 32 if(left.m_id == right.m_id) 33 return false; 34 if(left.m_hot != right.m_hot) 35 return left.m_hot > right.m_hot; //按熱度降序 36 else 37 return left.m_id > right.m_id; //熱度相同,按id降序 38 } 39 }; 40 int main(int argc,char* argv[]) 41 { 42 /*自定義比較函式的用法*/ 43 /*過載<運算子的用法*/ 44 //set<song> mySet; 45 //song s1(10,100); 46 //song s2(20,200); 47 //song s3(20,300); 48 //song s4(30,200); 49 //mySet.insert(s1); //插入s1 50 //mySet.insert(s2); //插入s2 51 //mySet.insert(s3); //s3和s2的id相同,不插入 52 //mySet.insert(s4); //插入s4 53 //for(auto it : mySet) 54 //{ 55 // cout << "id:" << it.m_id << ",hot:" << it.m_hot << endl; 56 //} 57 //cout << "end" << endl; 58 //return 0; 59 60 /*過載()運算子的用法*/ 61 set<song,comp> mySet; 62 song s1(10,100); 63 song s2(20,200); 64 song s3(20,300); 65 song s4(30,200); 66 mySet.insert(s1); //插入s1 67 mySet.insert(s2); //插入s2 68 mySet.insert(s3); //s3和s2的id相同,不插入 69 mySet.insert(s4); //插入s4 70 for(auto it : mySet) 71 { 72 cout << "id:" << it.m_id << ",hot:" << it.m_hot << endl; 73 } 74 cout << "end" << endl; 75 return 0; 76 }
執行結果: