【STL】set和multiset的初步認知
set/multiset是一個集合容器,我們可以對這個容器進行插入,刪除,查詢等工作。set的元素內容只有一個鍵值(key,key和value為同一個值),不允許重複冗餘,當對它插入一個已經存在的元素時,它會自動忽略。set/multiset的底層是用紅黑樹(RBTree)實現,擁有平衡二叉搜尋樹的結構,所以在進行檢索時,效率會很高。而正因為它是一顆紅黑樹結構,當我們順序遍歷它時,序列是有序的。我們就不能通過迭代器取修改相應的元素的key值。我們可以借原始碼看到,set的迭代器是紅黑樹const迭代器的typedef。
multiset大部分功能與set相同,不同之處在於multiset允許出現相同的key值,也就是說允許出現重複的元素。所以再insert,erase的實現上會有所不同。其它都一樣。我會再講到insert,erase時剖析一下不同點,下面以set開講。
構造/解構函式/賦值運算子過載
它的建構函式實現有三種。
建構函式
explicit set ( const Compare& comp = Compare(), const Allocator& = Allocator() );`
explicit關鍵字用來修飾類的建構函式,防止發生隱式的型別轉換。也可以理解為防止建構函式被隱式呼叫。
它有兩個引數,Compare是一個型別,Compare()是這個型別的匿名物件。這是一個比較函式,內部是以仿函式的形式來使用它,來實現排序時比較等功能。Allocator是一個空間配置器,它是c++標準庫中最神祕的存在。以我現在的水平,還分析不出它。但是對我們理解set也不會有影響。
2.
template <class InputIterator>
set ( InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator() );
這個建構函式是以模板形式定義。模板引數InputIterator是一個型別的迭代器。
它有四個引數,前兩個引數都是迭代器,(first,last)所表示的是一個左閉右開的區間。可想而知,這個建構函式是以其他容器的一段資料進行構造。後兩個引數與上相同。
3.
最後一個,拷貝建構函式,不再多說。
set ( const set<Key,Compare,Allocator>& x );
解構函式
~set()
賦值運算子過載
set<Key,Compare,Allocator>& operator= ( const set<Key,Compare,Allocator>& x );
返回值為一個set的引用,引數為另一個set的引用。
迭代器Iterators:
begin
iterator begin ();//非const
const_iterator begin () const;//const
正向迭代器,返回第一個元素的位置。
end
iterator end ();// 非const
const_iterator end () const; //const
正向迭代器,返回最後一個元素的下一個位置。
rbegin
reverse_iterator rbegin();.//非const
const_reverse_iterator rbegin() const; //const
反向迭代器,返回最後一個元素的位置。
rend
reverse_iterator rend();//非const
const_reverse_iterator rend() const;//const
反向迭代器,返回第一個元素的前一個位置。
注意:有人可能以為,我呼叫rbegin()得到的迭代器是最後一個元素的位置,那對這個迭代器進行–(減減)操作就可以從後向前遍歷這個set?不是,還是++(加加)。看一個例子:
#include<iostream>
#include<set>
using namespace std;
void TestSet()
{
set<int> s;
for (int i = 0; i < 10; ++i)
{
s.insert(i);
}
set<int>::reverse_iterator it = s.rbegin();//用反向迭代器接收
while (it != s.rend())
{
cout << *it << " ";
++it;//使用++從前向後遍歷
}
cout << endl;
}
int main()
{
TestSet();
system("pause");
return 0;
}
輸出結果:
Capacity:
empty
bool empty ( ) const;
判空,當為空時返回true,不空時返回false。加const修飾是防止這個函式對類的成員做出修改。
size
size_type size() const;
返回set內元素的數量。 const作用同上。
max_size
size_type size() const;
返回能插入元素的最大值,這個數輸出來應該是214748364。為什麼是這個數,由於系統和庫的限制。
調節器Modifiers
insert
set的insert有3種實現
pair<iterator,bool> insert ( const value_type& x );
返回值為一個pair,pair是一個模板型別,有兩個模板引數,第一個叫first,第二個叫second。(想深入瞭解的自己查一下)
這裡的first是一個迭代器,第二個引數是一個bool值。
當插入一個新元素時,返回值的first是新插入元素位置的迭代器,second是true。
當插入一個已有的元素時,不插入,返回值得first是已有元素位置的迭代器,second是false。
2.
iterator insert ( iterator position, const value_type& x );
第二種實現的返回值是一個迭代器,第一個引數是一個迭代器位置,第二個引數是要插入的元素。意思是在postion位置插入x。
當插入一個新元素,返回新元素的迭代器。
當插入一個已存在元素,不插入,返回已存在元素的迭代器。
3.
template <class InputIterator>
void insert ( InputIterator first, InputIterator last );
第3種實現,返回值為空,引數是兩個模板型別的迭代器,(first,last)是一個左閉右開的區間,意思是將這個區間的元素插入set。可以理解為批量插入。
multiset的insert
multiset的insert定義,返回值,引數與set全部相同,不同之處在於它們耳朵內部實現。我們在開頭說過set/multiset的底層實現是紅黑樹,所以它們大部分的功能函式都是呼叫的紅黑樹的函式。我們來看一下insert原始碼的定義:(參照《STL原始碼剖析》)
set的原始碼
(ps:t是一個紅黑樹的物件)
我們可以從圖中看到,set的insert的三種實現都呼叫了一個叫insert_unipue()的函式,我們不需要看它的實現了,從字面就可以看出,unique獨一無二,這種插入方法不允許出現重複資料。
multiset原始碼
(ps:注意紅色箭頭指向的內容,意思是其他函式的實現,multise與set相同)
可以看出不同了吧,multiset底層呼叫的insert_equal()這個函式。equal什麼意思呢?英語不好的就去查吧!^v^!
erase
set的erase三種實現。
void erase ( iterator position );
指定位置擦除,引數為一個迭代器,意思是擦拭position位置的資料。
2.
size_type erase ( const key_type& x );
指定元素擦除,引數為一個數據,意思是擦除鍵值(key)為x的資料。
3.
void erase ( iterator first, iterator last );
指定區間擦除,擦除左閉右開區間(first,last)的元素。
multiset的erase
我們已經知道了,multiset允許插入相同的元素,刪除呢?
1.
void erase ( iterator position );
指定位置刪除,與set相同
2.
void erase ( iterator first, iterator last );
刪除區間 [first,last),與set相同。
3.
不同的來了!
size_type erase ( const key_type& x );
指定元素刪除,刪除引數x。刪除key等於x的所有元素。返回刪除元素的個數。
clear
void clear ( );
清空所有元素,它內部呼叫的紅黑樹的clear。
swap
void swap ( set<Key,Compare,Allocator>& st );
交換兩個set的值,返回值為空,引數是另一個set的引用。
Observers
key_comp
key_compare key_comp ( ) const;
這個方法的功能是返回set排序所呼叫的比較函式。返回值是key_compare,什麼是key_compare呢?
檢視key_compare的定義,發現它是_Pr的一個typedef
再檢視_Pr的定義,
到這裡我們就能看到,_Pr是一個名為less的型別。再檢視less的定義。
一目瞭然了吧,less就是一個結構體,它內部實現了對()的過載,功能是比較兩個值得大小,left小於right的時候返回true。當把這個結構體型別傳入當模版引數時,在類內部就可以以仿函式的形式呼叫它。
再回來說到,key_compare就是一個型別,key_comp的返回值就是set的比較函式,也可以說是底層紅黑樹的比較函式。當然我們還可以呼叫這個比較函式。我們來測一下:
set<int>::key_compare ret2 = s.key_comp();
cout << "less:1<2?" << endl;
cout << ret2(1, 2) << endl;
cout << "less:1>2?" << endl;
cout << ret2(2, 1) << endl;
結果:
value_comp
set的key和value都是同一個值,都是鍵值(key),所以這個函式與key_comp的功能一樣,返回值也相同。
Operations
find
iterator find ( const key_type& x ) const;
查詢一個值x的位置,返回值為迭代器。const保證這個函式內部不對類的成員做出修改。
當這個值x存在時,返回這個值的迭代器。
當這個值x不存在時,返回end(最後一個元素的下一個位置),end是一個不可訪問的位置。
set/multiset的find的find函式功能都相同,返回的都是指向第一個匹配元素位置的迭代器。
count
size_type count ( const key_type& x ) const;
查詢一個元素是否存在。返回值型別為size_type(我們理解為size_t)
如果元素存在,返回元素的個數。
如果元素不存在,返回0。
思考:既然set裡面不允許出現相同的元素,次數只能是0或1,為什麼它要用一個size_type來接收返回值?為什麼不用bool?
因為multiset與set的count函式相同,底層都是呼叫的紅黑樹的count函式,multiset允許重複資料出現,那就有大於1的次數。所以用size_type。
lower_bound
iterator lower_bound ( const key_type& x ) const;
返回值是一個迭代器,返回指向set中第一個大於等於x的值的迭代器。當沒有比x大的元素的時候,返回end。
看一個例子:
我插入的原始資料為 1, 3, 5, 7 ,9
set<int> s;
for (int i = 1; i < 10; i+=2)
{
s.insert(i);
}
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
cout << "lower_bound: 大於等於0的第一個元素" << endl;
cout << *(s.lower_bound(0)) << endl;
cout << "lower_bound: 大於等於1的第一個元素" << endl;
cout << *(s.lower_bound(1)) << endl;
cout << "lower_bound: 大於等於2的第一個元素" << endl;
cout << *(s.lower_bound(2)) << endl;
cout << "lower_bound: 大於等於3的第一個元素" << endl;
cout << *(s.lower_bound(4)) << endl;
cout << "lower_bound: 大於等於4的第一個元素" << endl;
cout << *(s.lower_bound(6)) << endl;
結果:
upper_bound
iterator upper_bound ( const key_type& x ) const;
返回指向第一個大於x的值的迭代器,例子不再給出,測試方法與上相同。
equal_range
pair<iterator,iterator> equal_range ( const key_type& x ) const;
返回值為一個pair,pair的兩個值都是迭代器,這個函式的功能是上面兩個函式的結合體。找到指向第一個大於等於x的值的迭代器,存到pair的first裡面去。找到指向第一個大於x的值的迭代器,存到pair的second裡面去。
看一個例子,插入資料1,3,5,7,9
set<int> s;
for (int i = 1; i < 10; i += 2)
{
s.insert(i);
}
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
pair<set<int>::iterator, set<int>::iterator> ret = s.equal_range(3);
cout << "pair.first:lower_bound:" << *ret.first << endl;
cout << "pair.second:upper_bound:" << *ret.second << endl;
結果:
Allocator
空間配置器等我搞明白了再寫出來。。。。。