set集合容器:
實現了紅黑樹(Red-Black Tree)的平衡二叉檢索樹的數據結構,在插入元素時,
它會自動調整二叉樹的排列,把該元素放到適當的位置,以確保每個子樹根節點的鍵值大於左子樹所有節點的鍵值,
而小於右子樹所有節點的鍵值;另外,還得確保根節點的左子樹的高度與有字數的高度相等,
這樣,二叉樹的高度最小,從而檢索速度最快。要注意的是,它不會重復插入相同鍵值的元素,而采取忽略處理。
平衡二叉檢索樹的檢索使用中序遍歷算法,檢索效率高於vector、deque、和list的容器。
另外,采用中序遍歷算法可將鍵值由小到大遍歷出來,所以,可以理解為平衡二叉檢索樹在插入元素時,
就會自動將元素按鍵值從小到大的順序排列。
構造set集合的主要目的是為了快速檢索,使用set前,需要在程序頭文件中包含聲明“#include<set>”。
c++ stl集合(Set):
是一種包含已排序對象的關聯容器。set/multiset會根據待定的排序准則,
自動將元素排序。兩者不同在於前者不允許元素重復,而后者允許。
1) 不能直接改變元素值,因為那樣會打亂原本正確的順序,要改變元素值必須先刪除舊元素,則插入新元素
2) 不提供直接存取元素的任何操作函數,只能通過迭代器進行間接存取,而且從迭代器角度來看,元素值是常數
3) 元素比較動作只能用於型別相同的容器(即元素和排序准則必須相同)
set的各成員函數列表如下:
begin()--返回指向第一個元素的迭代器
clear()--清除所有元素
count()--返回某個值元素的個數
empty()--如果集合為空,返回true
end()--返回指向最后一個元素的迭代器
equal_range()--返回集合中與給定值相等的上下限的兩個迭代器
erase()--刪除集合中的元素
find()--返回一個指向被查找到元素的迭代器
get_allocator()--返回集合的分配器
insert()--在集合中插入元素
lower_bound()--返回指向大於(或等於)某值的第一個元素的迭代器
key_comp()--返回一個用於元素間值比較的函數
max_size()--返回集合能容納的元素的最大限值
rbegin()--返回指向集合中最后一個元素的反向迭代器
rend()--返回指向集合中第一個元素的反向迭代器
size()--集合中元素的數目
swap()--交換兩個集合變量
upper_bound()--返回大於某個值元素的迭代器
value_comp()--返回一個用於比較元素間的值的函數
1.創建set集合對象
創建set對象時,需要指定元素的類型,這一點和其他容器一樣。
1 #include<iostream> 2 #include<set> 3 using namespace std; 4 int main() 5 { 6 set<int> s; 7 return 0; 8 }
2.元素的插入與中序遍歷
采用inset()方法把元素插入到集合中,插入規則在默認的比較規則下,
是按元素值從小到大插入,如果自己指定了比較規則函數,則按自定義比較規則函數插入。
使用前向迭代器對集合中序遍歷,結果正好是元素排序后的結果。
1 #include<iostream> 2 #include<set> 3 using namespace std; 4 int main() 5 { 6 set<int> s; 7 s.insert(5); //第一次插入5,可以插入 8 s.insert(1); 9 s.insert(6); 10 s.insert(3); 11 s.insert(5); //第二次插入5,重復元素,不會插入 12 set<int>::iterator it; //定義前向迭代器 13 //中序遍歷集合中的所有元素 14 for(it = s.begin(); it != s.end(); it++) 15 { 16 cout << *it << " "; 17 } 18 cout << endl; 19 return 0; 20 } 21 //運行結果:1 3 5 6
3.元素的方向遍歷
使用反向迭代器reverse_iterator可以反向遍歷集合,輸出的結果正好是集合元素的反向排序結果。
它需要用到rbegin()和rend()兩個方法,它們分別給出了反向遍歷的開始位置和結束位置。
1 #include<iostream> 2 #include<set> 3 using namespace std; 4 int main() 5 { 6 set<int> s; 7 s.insert(5); //第一次插入5,可以插入 8 s.insert(1); 9 s.insert(6); 10 s.insert(3); 11 s.insert(5); //第二次插入5,重復元素,不會插入 12 set<int>::reverse_iterator rit; //定義反向迭代器 13 //反向遍歷集合中的所有元素 14 for(rit = s.rbegin(); rit != s.rend(); rit++) 15 { 16 cout << *rit << " "; 17 } 18 cout << endl; 19 return 0; 20 } 21 //運行結果:6 5 3 1
4.元素的刪除
與插入元素的處理一樣,集合具有高效的刪除處理功能,並自動重新調整內部的紅黑樹的平衡。
刪除的對象可以是某個迭代器位置上的元素、等於某鍵值的元素、一個區間上的元素和清空集合。
1 #include<iostream> 2 #include<set> 3 using namespace std; 4 int main() 5 { 6 set<int> s; 7 s.insert(5); //第一次插入5,可以插入 8 s.insert(1); 9 s.insert(6); 10 s.insert(3); 11 s.insert(5); //第二次插入5,重復元素,不會插入 12 s.erase(6); //刪除鍵值為6的元素 13 set<int>::reverse_iterator rit; //定義反向迭代器 14 //反向遍歷集合中的所有元素 15 for(rit = s.rbegin(); rit != s.rend(); rit++) 16 { 17 cout << *rit << " "; 18 } 19 cout << endl; 20 set<int>::iterator it; 21 22 it = s.begin(); 23 for(int i = 0; i < 2; i++) 24 it = s.erase(it); 25 for(it = s.begin(); it != s.end(); it++) 26 cout << *it << " "; 27 cout << endl; 28 29 s.clear(); 30 cout << s.size() << endl; 31 32 return 0; 33 } 34 /* 35 運行結果: 36 5 3 1 37 5 38 0 39 */
5.元素的檢索
使用find()方法對集合進行檢索,如果找到查找的的鍵值,則返回該鍵值的迭代器位置;
否則,返回集合最后一個元素后面的一個位置,即end()。
1 #include<iostream> 2 #include<set> 3 using namespace std; 4 int main() 5 { 6 set<int> s; 7 s.insert(5); //第一次插入5,可以插入 8 s.insert(1); 9 s.insert(6); 10 s.insert(3); 11 s.insert(5); //第二次插入5,重復元素,不會插入 12 set<int>::iterator it; 13 it = s.find(6); //查找鍵值為6的元素 14 if(it != s.end()) 15 cout << *it << endl; 16 else 17 cout << "not find it" << endl; 18 it = s.find(20); 19 if(it != s.end()) 20 cout << *it << endl; 21 else 22 cout << "not find it" << endl; 23 return 0; 24 } 25 /* 26 運行結果: 27 6 28 not find it 29 */
下面這種方法也能判斷一個數是否在集合中:
1 #include <cstdio> 2 #include <set> 3 using namespace std; 4 int main() { 5 set <int> s; 6 int a; 7 for(int i = 0; i < 10; i++) 8 s.insert(i); 9 for(int i = 0; i < 5; i++) { 10 scanf("%d", &a); 11 if(!s.count(a)) //不存在 12 printf("does not exist\n"); 13 else 14 printf("exist\n"); 15 } 16 return 0; 17 }
6.自定義比較函數
使用insert將元素插入到集合中去的時候,集合會根據設定的比較函數獎該元素放到該放的節點上去。
在定義集合的時候,如果沒有指定比較函數,那么采用默認的比較函數,即按鍵值從小到大的順序插入元素。
但在很多情況下,需要自己編寫比較函數。
編寫比較函數有兩種方法。
(1)如果元素不是結構體,那么可以編寫比較函數。下面的程序比較規則為按鍵值從大到小的順序插入到集合中。
1 #include<iostream> 2 #include<set> 3 using namespace std; 4 struct mycomp 5 { //自定義比較函數,重載“()”操作符 6 bool operator() (const int &a, const int &b) 7 { 8 if(a != b) 9 return a > b; 10 else 11 return a > b; 12 } 13 }; 14 int main() 15 { 16 set<int, mycomp> s; //采用比較函數mycomp 17 s.insert(5); //第一次插入5,可以插入 18 s.insert(1); 19 s.insert(6); 20 s.insert(3); 21 s.insert(5); //第二次插入5,重復元素,不會插入 22 set<int,mycomp>::iterator it; 23 for(it = s.begin(); it != s.end(); it++) 24 cout << *it << " "; 25 cout << endl; 26 return 0; 27 } 28 /* 29 運行結果:6 5 3 1 30 */
(2)如果元素是結構體,那么可以直接把比較函數寫在結構體內。
1 #include<iostream> 2 #include<set> 3 #include<string> 4 using namespace std; 5 struct Info 6 { 7 string name; 8 double score; 9 bool operator < (const Info &a) const // 重載“<”操作符,自定義排序規則 10 { 11 //按score由大到小排序。如果要由小到大排序,使用“>”即可。 12 return a.score < score; 13 } 14 }; 15 int main() 16 { 17 set<Info> s; 18 Info info; 19 20 //插入三個元素 21 info.name = "Jack"; 22 info.score = 80; 23 s.insert(info); 24 info.name = "Tom"; 25 info.score = 99; 26 s.insert(info); 27 info.name = "Steaven"; 28 info.score = 60; 29 s.insert(info); 30 31 set<Info>::iterator it; 32 for(it = s.begin(); it != s.end(); it++) 33 cout << (*it).name << " : " << (*it).score << endl; 34 return 0; 35 } 36 /* 37 運行結果: 38 Tom : 99 39 Jack : 80 40 Steaven : 60 41 */