C++中map和set的使用與區別
set
set是一種關聯式容器,其特性如下:
- set以RBTree作為底層容器
- 所得元素的只有key沒有value,value就是key
- 不允許出現鍵值重複
- 所有的元素都會被自動排序
- 不能通過迭代器來改變set的值,因為set的值就是鍵
針對這五點來說,前四點都不用再多作說明,第五點需要做一下說明。如果set中允許修改鍵值的話,那麼首先需要刪除該鍵,然後調節平衡,在插入修改後的鍵值,再調節平衡,如此一來,嚴重破壞了set的結構,導致iterator失效,不知道應該指向之前的位置,還是指向改變後的位置。所以STL中將set的迭代器設定成const,不允許修改迭代器的值。
set的資料結構
// 比較器預設採用less,內部按照升序排列,配置器預設採用alloc template <class Key, class Compare = less<Key>, class Alloc = alloc> class set { public: // 在set中key就是value, value同時也是key typedef Key key_type; typedef Key value_type; // 用於比較的函式 typedef Compare key_compare; typedef Compare value_compare; private: // 內部採用RBTree作為底層容器 typedef rb_tree<key_type, value_type, identity<value_type>, key_compare, Alloc> rep_type; rep_type t; // t為內部RBTree容器 public: // 用於提供iterator_traits<I>支援 typedef typename rep_type::const_pointer pointer; typedef typename rep_type::const_pointer const_pointer; typedef typename rep_type::const_reference reference; typedef typename rep_type::const_reference const_reference; typedef typename rep_type::difference_type difference_type; // 設定成const迭代器,set的鍵值不允許修改 typedef typename rep_type::const_iterator iterator; typedef typename rep_type::const_iterator const_iterator; // 反向迭代器 typedef typename rep_type::const_reverse_iterator reverse_iterator; typedef typename rep_type::const_reverse_iterator const_reverse_iterator; typedef typename rep_type::size_type size_type; iterator begin() const { return t.begin(); } iterator end() const { return t.end(); } reverse_iterator rbegin() const { return t.rbegin(); } reverse_iterator rend() const { return t.rend(); } bool empty() const { return t.empty(); } size_type size() const { return t.size(); } size_type max_size() const { return t.max_size(); } // 返回用於key比較的函式 key_compare key_comp() const { return t.key_comp(); } // 由於set的性質, value比較和key使用同一個比較函式 value_compare value_comp() const { return t.key_comp(); } // 聲明瞭兩個友元函式,過載了==和<操作符 friend bool operator== __STL_NULL_TMPL_ARGS (const set&, const set&); friend bool operator< __STL_NULL_TMPL_ARGS (const set&, const set&); // ... }
set的建構函式
set提供瞭如下幾個建構函式用於初始化一個set
// 注:下面相關函式都在set類中定義,為了介紹方便才抽出來單獨講解 // 空建構函式,初始化一個空的set set() : t(Compare()) {} // 支援自定義比較器,如set<int,greater<int> > myset的初始化 explicit set(const Compare& comp) : t(comp) {} // 實現諸如set<int> myset(anotherset.begin(),anotherset.end())這樣的初始化 template <class InputIterator> set(InputIterator first, InputIterator last) : t(Compare()) { t.insert_unique(first, last); } // 支援自定義比較器的初始化操作 template <class InputIterator> set(InputIterator first, InputIterator last, const Compare& comp) : t(comp) { t.insert_unique(first, last); } // 以另一個set來初始化 set(const set<Key, Compare, Alloc>& x) : t(x.t) {} // 賦值運算子函式 set<Key, Compare, Alloc>& operator=(const set<Key, Compare, Alloc>& x) { t = x.t; return *this; }
set的操作函式
insert
插入函式,呼叫RBTree的插入函式即可typedef pair<iterator, bool> pair_iterator_bool;
// 由於set不允許鍵值重複,所以必須呼叫RBTree的insert_unique函式
// second表示插入操作是否成功
pair<iterator,bool> insert(const value_type& x)
{
pair<typename rep_type::iterator, bool> p = t.insert_unique(x);
return pair<iterator, bool>(p.first, p.second);
}
// 在position處插入元素, 但是position僅僅是個提示, 如果給出的位置不能進行插入,
// STL會進行查詢, 這會導致很差的效率
iterator insert(iterator position, const value_type& x)
{
typedef typename rep_type::iterator rep_iterator;
return t.insert_unique((rep_iterator&)position, x);
}
// 將[first,last)區間內的元素插入到set中
template <class InputIterator>
void insert(InputIterator first, InputIterator last)
{
t.insert_unique(first, last);
}
erase
擦除函式,用於擦除單個元素或者區間內的元素,直接呼叫RBTree的函式即可
// 擦除指定位置的元素, 會導致內部的紅黑樹重新排列
void erase(iterator position)
{
typedef typename rep_type::iterator rep_iterator;
t.erase((rep_iterator&)position);
}
// 會返回擦除元素的個數, 其實就是標識set內原來是否有指定的元素
size_type erase(const key_type& x)
{
return t.erase(x);
}
// 擦除指定區間的元素, 會導致紅黑樹有較大變化
void erase(iterator first, iterator last)
{
typedef typename rep_type::iterator rep_iterator;
t.erase((rep_iterator&)first, (rep_iterator&)last);
}
clean
清除整個set容器,直接呼叫RBTree的clean函式即可void clear() { t.clear(); }
find
查詢函式,RBTree也提供了,直接呼叫即可
// 查詢指定的元素
iterator find(const key_type& x) const { return t.find(x); }
count
查詢制定元素的個數// 返回指定元素的個數, set不允許鍵值重複,其實就是測試元素是否在set中
size_type count(const key_type& x) const { return t.count(x); }
過載操作符
set過載了==和<操作符,基本上都是呼叫RBTree的介面函式即可,如下所示:
template <class Key, class Compare, class Alloc>
inline bool operator==(const set<Key, Compare, Alloc>& x,
const set<Key, Compare, Alloc>& y) {
return x.t == y.t;
}
template <class Key, class Compare, class Alloc>
inline bool operator<(const set<Key, Compare, Alloc>& x,
const set<Key, Compare, Alloc>& y) {
return x.t < y.t;
}
其他操作函式
// 返回小於當前元素的第一個可插入的位置
iterator lower_bound(const key_type& x) const
{
return t.lower_bound(x);
}
// 返回大於當前元素的第一個可插入的位置
iterator upper_bound(const key_type& x) const
{
return t.upper_bound(x);
}
// 返回與指定鍵值相等的元素區間
pair<iterator,iterator> equal_range(const key_type& x) const
{
return t.equal_range(x);
}
multiset
multiset相對於set來說,區別就是multiset允許鍵值重複,在multiset中呼叫的是RBTree的insert_equal函式,其他的基本與set相同。
其他的就不贅述了,下面列舉一下跟set不同的地方:
// 初始化函式,
// 注意!!!!插入操作採用的是RBTree的insert_equal,而不是insert_unique
template <class InputIterator>
multiset(InputIterator first, InputIterator last)
: t(Compare()) { t.insert_equal(first, last); }
template <class InputIterator>
multiset(InputIterator first, InputIterator last, const Compare& comp)
: t(comp) { t.insert_equal(first, last); }
// 插入元素, 注意, 插入的元素key允許重複
iterator insert(const value_type& x)
{
return t.insert_equal(x);
}
// 在position處插入元素, 但是position僅僅是個提示, 如果給出的位置不能進行插入,
// STL會進行查詢, 這會導致很差的效率
iterator insert(iterator position, const value_type& x)
{
typedef typename rep_type::iterator rep_iterator;
return t.insert_equal((rep_iterator&)position, x);
}
map
map和set一樣是關聯式容器,它們的底層容器都是紅黑樹,區別就在於map的值不作為鍵,鍵和值是分開的。它的特性如下:
- map以RBTree作為底層容器
- 所有元素都是鍵+值存在
- 不允許鍵重複
- 所有元素是通過鍵進行自動排序的
- map的鍵是不能修改的,但是其鍵對應的值是可以修改的
在map中,一個鍵對應一個值,其中鍵不允許重複,不允許修改,但是鍵對應的值是可以修改的,原因可以看上面set中的解釋。下面就一起來看看STL中的map的原始碼。
map的資料結構
// 預設比較器為less<key>,元素按照鍵的大小升序排列
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:
typedef Key key_type; // key型別
typedef T data_type; // value型別
typedef T mapped_type;
typedef pair<const Key, T> value_type; // 元素型別, 要保證key不被修改
typedef Compare key_compare; // 用於key比較的函式
private:
// 內部採用RBTree作為底層容器
typedef rb_tree<key_type, value_type,
identity<value_type>, key_compare, Alloc> rep_type;
rep_type t; // t為內部RBTree容器
public:
// 用於提供iterator_traits<I>支援
typedef typename rep_type::const_pointer pointer;
typedef typename rep_type::const_pointer const_pointer;
typedef typename rep_type::const_reference reference;
typedef typename rep_type::const_reference const_reference;
typedef typename rep_type::difference_type difference_type;
// 注意:這裡與set不一樣,map的迭代器是可以修改的
typedef typename rep_type::iterator iterator;
typedef typename rep_type::const_iterator const_iterator;
// 反向迭代器
typedef typename rep_type::const_reverse_iterator reverse_iterator;
typedef typename rep_type::const_reverse_iterator const_reverse_iterator;
typedef typename rep_type::size_type size_type;
// 常規的返回迭代器函式
iterator begin() { return t.begin(); }
const_iterator begin() const { return t.begin(); }
iterator end() { return t.end(); }
const_iterator end() const { return t.end(); }
reverse_iterator rbegin() { return t.rbegin(); }
const_reverse_iterator rbegin() const { return t.rbegin(); }
reverse_iterator rend() { return t.rend(); }
const_reverse_iterator rend() const { return t.rend(); }
bool empty() const { return t.empty(); }
size_type size() const { return t.size(); }
size_type max_size() const { return t.max_size(); }
// 返回用於key比較的函式
key_compare key_comp() const { return t.key_comp(); }
// 由於map的性質, value和key使用同一個比較函式, 實際上我們並不使用value比較函式
value_compare value_comp() const { return value_compare(t.key_comp()); }
// 注意: 這裡有一個常見的陷阱, 如果訪問的key不存在, 會新建立一個
T& operator[](const key_type& k)
{
return (*((insert(value_type(k, T()))).first)).second;
}
// 過載了==和<操作符,後面會有實現
friend bool operator== __STL_NULL_TMPL_ARGS (const map&, const map&);
friend bool operator< __STL_NULL_TMPL_ARGS (const map&, const map&);
}
map的建構函式
map提供了一下的建構函式來初始化一個map
// 空建構函式,直接呼叫RBTree的空建構函式
map() : t(Compare()) {}
explicit map(const Compare& comp) : t(comp) {}
// 提供類似map<int,int> myMap(anotherMap.begin(),anotherMap.end())的初始化
template <class InputIterator>
map(InputIterator first, InputIterator last)
: t(Compare()) { t.insert_unique(first, last); }
// 提供類似map<int,int> myMap(anotherMap.begin(),anotherMap.end(),less<int>)初始化
template <class InputIterator>
map(InputIterator first, InputIterator last, const Compare& comp)
: t(comp) { t.insert_unique(first, last); }
// 提供類似map<int> maMap(anotherMap)的初始化
map(const map<Key, T, Compare, Alloc>& x) : t(x.t) {}
// 過載=操作符,賦值運算子
map<Key, T, Compare, Alloc>& operator=(const map<Key, T, Compare, Alloc>& x)
{
t = x.t;
return *this;
}
map的操作函式
insert
同set一樣,直接呼叫RBTree的插入函式即可,注意map不允許鍵值重複,所以呼叫的是insert_unique
// 對於相同的key, 只允許出現一次, bool標識
pair<iterator,bool> insert(const value_type& x) { return t.insert_unique(x); }
// 在position處
插入元素, 但是position僅僅是個提示, 如果給出的位置不能進行插入,
// STL會進行查詢, 這會導致很差的效率
iterator insert(iterator position, const value_type& x)
{
return t.insert_unique(position, x);
}
// 將[first,last)區間內的元素插入到map中
template <class InputIterator>
void insert(InputIterator first, InputIterator last) {
t.insert_unique(first, last);
}
erase
同set,直接呼叫即可// 擦除指定位置的元素, 會導致內部的紅黑樹重新排列
void erase(iterator position) { t.erase(position); }
// 會返回擦除元素的個數, 其實就是標識map內原來是否有指定的元素
size_type erase(const key_type& x) { return t.erase(x); }
void erase(iterator first, iterator last) { t.erase(first, last); }
clean
同set,直接呼叫即可
void clear() { t.clear(); }
find
// 查詢指定key的元素
iterator find(const key_type& x) { return t.find(x); }
const_iterator find(const key_type& x) const { return t.find(x); }
````
過載運算子
上面介紹到map過載了[],==和<運算子,[]的實現已經介紹過,下面是==和<的實現
// 比較map直接是對其底層容器t的比較,直接呼叫RBTree的比較函式即可
template <class Key, class T, class Compare, class Alloc>
inline bool operator==(const map<Key, T, Compare, Alloc>& x,
const map<Key, T, Compare, Alloc>& y)
{
return x.t == y.t;
}
template <class Key, class T, class Compare, class Alloc>
inline bool operator<(const map<Key, T, Compare, Alloc>& x,
const map<Key, T, Compare, Alloc>& y)
{
return x.t < y.t;
}
其他操作函式
// 返回小於當前元素的第一個可插入的位置
iterator lower_bound(const key_type& x) {return t.lower_bound(x); }
const_iterator lower_bound(const key_type& x) const
{
return t.lower_bound(x);
}
// 返回大於當前元素的第一個可插入的位置
iterator upper_bound(const key_type& x) {return t.upper_bound(x); }
const_iterator upper_bound(const key_type& x) const
{
return t.upper_bound(x);
}
// 返回與指定鍵值相等的元素區間
pair<iterator,iterator> equal_range(const key_type& x)
{
return t.equal_range(x);
}
multimap
multimap和map的關係就跟multiset和set的關係一樣,multimap允許鍵的值相同,因此在插入操作的時候用到insert_equal(),除此之外,基本上與map相同。
下面就僅僅列出不同的地方
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class multimap
{
// ... 其他地方與map相同
// 注意下面這些函式都呼叫的是insert_equal,而不是insert_unique
template <class InputIterator>
multimap(InputIterator first, InputIterator last)
: t(Compare()) { t.insert_equal(first, last); }
template <class InputIterator>
multimap(InputIterator first, InputIterator last, const Compare& comp)
: t(comp) { t.insert_equal(first, last); }
// 插入元素, 注意, 插入的元素key允許重複
iterator insert(const value_type& x) { return t.insert_equal(x); }
// 在position處插入元素, 但是position僅僅是個提示, 如果給出的位置不能進行插入,
// STL會進行查詢, 這會導致很差的效率
iterator insert(iterator position, const value_type& x)
{
return t.insert_equal(position, x);
}
// 插入一個區間內的元素
template <class InputIterator>
void insert(InputIterator first, InputIterator last)
{
t.insert_equal(first, last);
}
// ...其餘地方和map相同
}
總結
總的來說,這四類容器僅僅只是在RBTree上進行了一層封裝,首先,set和map的區別就在於鍵和值是否相同,set中將值作為鍵,支援STL的提供的一些交集、並集和差集等運算;map的鍵和值不同,每個鍵都有自己的值,鍵不能重複,但是值可以重複。
multimap和multiset就在map和set的基礎上,使他們的鍵可以重複,除此之外基本等同。
---------------------
作者:zy20150613
來源:CSDN
原文:https://blog.csdn.net/zy20150613/article/details/78693579
版權宣告:本文為博主原創文章,轉載請附上博文連結!