C++ -- 学习系列 关联式容器 set 与 map

一   关联式容器是什么?

c++ 中有两种容器类型:关联式容器与序列式容器(顺序容器)

关联式中的容器是按照关键字来存储与访问的,序列式容器(顺序容器)则是元素在容器中的相对位置来存储与访问的。

c++ 中的关联式容器主要是 set 与 map.

二   底层原理与源码

1. 红黑树

       红黑树是一种平衡二叉搜索树(balanced binary search tree),即插入或者删除元素后,依然能够保证树是平衡的,所谓平衡意味着任意一个父节点,其左右子树的深度相差不会太多。

平衡树也称 AVL 树,任意节点的左右个子树的高度差不超过1。

这一特性也保证了在插入元素与查找元素时的效率。

红黑树核心法则:

1. 每个节点要么是红色,要么是黑色

2. 红黑树中的任意节点到达其每个叶子节点的黑色高度是相同的(黑色高度值得是某个节点到达叶子节点的黑色节点的个数,因叶子节点是黑色的,所以也包括叶子节点)

3. 两个红色节点之间不能相邻,即对于任意一个红色节点而言,其左右子节点定不是红色

4. 根节点必须是黑色的

5. 每个红色节点的两个子节点一定是黑色的

【红黑树】的详细实现(C++)附代码 - 知乎 (zhihu.com)

c++ 中的红黑树源代码位置

#include <bits/stl_tree.h
 template<typename _Key, typename _Val, typename _KeyOfValue,typename _Compare, typename _Alloc = allocator<_Val> >class _Rb_tree{typedef typename __gnu_cxx::__alloc_traits<_Alloc>::templaterebind<_Rb_tree_node<_Val> >::other _Node_allocator;typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;protected:typedef _Rb_tree_node_base* 		_Base_ptr;typedef const _Rb_tree_node_base* 	_Const_Base_ptr;typedef _Rb_tree_node<_Val>* 		_Link_type;typedef const _Rb_tree_node<_Val>*	_Const_Link_type;......};

源码中的模板参数解释如下:

1. Key 为存储在红黑树中的关键字类型

2. Value 实际存储数据的类型

3. KeyOfValue 表示如何通过 Value 获取到 Key,通常是一个函数

4. Compare 则为比较元素大小的函数,可自定义实现

5. Alloc 分配内存的方式

#include<iostream>
#include <bits/stl_tree.h>int main()
{
//    template<typename _Tp>
//      struct _Identity
//      : public unary_function<_Tp,_Tp>
//      {
//        _Tp&
//        operator()(_Tp& __x) const
//        { return __x; }//        const _Tp&
//        operator()(const _Tp& __x) const
//        { return __x; }
//      };std::_Rb_tree<int, int, std::_Identity<int>, std::less<int>> rb_tree;std::cout << rb_tree.empty() << std::endl;std::cout << rb_tree.size() << std::endl;// 1. 插入元素不允许重复.rb_tree._M_insert_unique(1);rb_tree._M_insert_unique(2);rb_tree._M_insert_unique(3);rb_tree._M_insert_unique(4);rb_tree._M_insert_unique(5);std::cout << rb_tree.size() << std::endl;rb_tree._M_insert_unique(1);std::cout << rb_tree.size() << std::endl;std::cout << "------" << std::endl;// 2. 插入元素允许重复.rb_tree._M_insert_equal(1);rb_tree._M_insert_equal(1);rb_tree._M_insert_equal(1);std::cout << rb_tree.size() << std::endl;for(auto iter = rb_tree.begin(); iter != rb_tree.end(); iter++){std::cout << *iter <<" ";}std::cout <<""<<std::endl;return 0;
}

2. 基于红黑树的关联式容器

2.1 set/multiset

set/multiset 是以红黑树为底层结构的,因此存入的元素具有自动排序的特性,排序的依据是 key ,而 set/miltiset  元素的 key 与 value是合二为一的,其value 就是 key;

set/multiset  提供遍历操作与迭代器 iterator, 通过 不断的 iterator++ 遍历可以获取到已经排好序的元素;

我们无法通过 迭代器来改变 set/multiset 的值,这样设计的原因是 若是可以随意修改值,那么按照key 排好的顺序便有可能不存在了,从代码上来讲,set/multiset 用的迭代器是底层红黑树类 _Rb_tree 的 const iterator ,禁止使用者赋值。

 2.1.1 set 源代码
template<typename _Key, typename _Compare = std::less<_Key>,typename _Alloc = std::allocator<_Key> >class set{public:// typedefs://@{/// Public typedefs.typedef _Key     key_type;typedef _Key     value_type;typedef _Compare key_compare;typedef _Compare value_compare;typedef _Alloc   allocator_type;private:typedef typename __gnu_cxx::__alloc_traits<_Alloc>::templaterebind<_Key>::other _Key_alloc_type;typedef _Rb_tree<key_type, value_type, _Identity<value_type>,key_compare, _Key_alloc_type> _Rep_type;_Rep_type _M_t;  // Red-black tree representing set.typedef __gnu_cxx::__alloc_traits<_Key_alloc_type> _Alloc_traits;.....iteratorinsert(const_iterator __position, const value_type& __x){ return _M_t._M_insert_unique_(__position, __x); }.....
};

通过源码可以看出,set 底层使用的是 _Rb_tree , insert 函数底层调用的是 _Rb_tree 的 insert_unique 函数,即 _Rb_tree 中的元素不重复。

2.1.2 multiset 源码
 template <typename _Key, typename _Compare = std::less<_Key>,typename _Alloc = std::allocator<_Key> >class multiset{
#ifdef _GLIBCXX_CONCEPT_CHECKS// concept requirementstypedef typename _Alloc::value_type		_Alloc_value_type;
# if __cplusplus < 201103L__glibcxx_class_requires(_Key, _SGIAssignableConcept)
# endif__glibcxx_class_requires4(_Compare, bool, _Key, _Key,_BinaryFunctionConcept)__glibcxx_class_requires2(_Key, _Alloc_value_type, _SameTypeConcept)
#endifpublic:// typedefs:typedef _Key     key_type;typedef _Key     value_type;typedef _Compare key_compare;typedef _Compare value_compare;typedef _Alloc   allocator_type;private:/// This turns a red-black tree into a [multi]set.typedef typename __gnu_cxx::__alloc_traits<_Alloc>::templaterebind<_Key>::other _Key_alloc_type;typedef _Rb_tree<key_type, value_type, _Identity<value_type>,key_compare, _Key_alloc_type> _Rep_type;/// The actual tree structure._Rep_type _M_t;typedef __gnu_cxx::__alloc_traits<_Key_alloc_type> _Alloc_traits;......iteratorinsert(const value_type& __x){ return _M_t._M_insert_equal(__x); }......};

通过源码可以看出,multiset 底层使用的是 _Rb_tree , insert 函数底层调用的是 _Rb_tree 的 insert_equal 函数,即 _Rb_tree 中的元素允许重复。

2.2 map/multimap

map/multimap 是以红黑树为底层结构的,因此存入的元素具有自动排序的特性,排序的依据是 key;

map/multimap 提供遍历操作与迭代器 iterator, 通过 不断的 iterator++ 遍历可以获取到已经按照 key 排好序的元素;

我们无法通过 迭代器来改变 map/multimap 的值,这样设计的原因是 若是可以随意修改值,那么按照 key 排好的顺序便有可能不存在了,但是我们可以修改 key 对应的 data 值。因而 map/multimap 内部将 key type 设为 const ,如此可以避免对 key 的随意修改。

map 的key 是独一无二的,所以底层使用 _Rb_tree 的 insert_unique 函数;

multimap 的key允许重复,所以底层使用 _Rb_tree 的 insert_equal 函数

2.2.1  map 源码
template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >class map{public:typedef _Key					key_type;typedef _Tp					mapped_type;typedef std::pair<const _Key, _Tp>		value_type;typedef _Compare					key_compare;typedef _Alloc					allocator_type;private:/// This turns a red-black tree into a [multi]map.typedef typename __gnu_cxx::__alloc_traits<_Alloc>::templaterebind<value_type>::other _Pair_alloc_type;typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,key_compare, _Pair_alloc_type> _Rep_type;/// The actual tree structure._Rep_type _M_t;typedef __gnu_cxx::__alloc_traits<_Pair_alloc_type> _Alloc_traits;.....std::pair<iterator, bool>insert(const value_type& __x){ return _M_t._M_insert_unique(__x); }.....
};

通过源码可以看到 map 的 insert 函数底层调用的是 insert_unique 函数,所以 map 的 key 是唯一的。

2.2.2 multimap 源码
template <typename _Key, typename _Tp,typename _Compare = std::less<_Key>,typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >class multimap{public:typedef _Key					key_type;typedef _Tp					mapped_type;typedef std::pair<const _Key, _Tp>		value_type;typedef _Compare					key_compare;typedef _Alloc					allocator_type;private:/// This turns a red-black tree into a [multi]map.typedef typename __gnu_cxx::__alloc_traits<_Alloc>::templaterebind<value_type>::other _Pair_alloc_type;typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,key_compare, _Pair_alloc_type> _Rep_type;/// The actual tree structure._Rep_type _M_t;typedef __gnu_cxx::__alloc_traits<_Pair_alloc_type> _Alloc_traits;......iteratorinsert(const value_type& __x){ return _M_t._M_insert_equal(__x); }......
};

通过源码可以看到 multimap 的 insert 函数底层调用的是 insert_equal 函数,所以 map 的 key 是可以重复的。

2.2.3  Select1st

前面的源码中提到了  Select1st,该函数的作用是获取 pair 中的第一个元素,应用在 map 中,获取的就是 key

Select1st 源码如下:

 template<typename _Pair>struct _Select1st: public unary_function<_Pair, typename _Pair::first_type>{typename _Pair::first_type&operator()(_Pair& __x) const{ return __x.first; }const typename _Pair::first_type&operator()(const _Pair& __x) const{ return __x.first; }
};

三  使用

1. set/multiset

  1.1 set 函数

std::set - cppreference.com       

   1.1.1  构造函数
函数说明
set()空构造函数
 template<typename _InputIterator>	set(_InputIterator __first, _InputIterator __last)
range 构造函数
    1.1.2  容器修改
函数说明
clear()清空容器
insert插入元素
emplace插入元素,可以只传入元素类的构造函数所需参数
erase移除指定位置的元素
    1.1.3  容器查找
函数说明
count返回指定元素的个数
begin()返回首元素的 iterator
end()返回尾元素下一地址的 iterator,该 iterator 不能获取元素
find查找指定元素,若是存在返回指向该元素的 iterator;若是不存在,则返回尾 iterator
lower_boundIterator pointing to the first element that is not less than key. If no such element is found, a past-the-end iterator (see end()) is returned.
uppser_bound

Iterator pointing to the first element that is greater than key. If no such element is found, past-the-end (see end()) iterator is returned.

1.1.4  容器容量
函数说明
empty()判断 set 是否为空
size()返回 set 中的元素个数
1.1.5  示例
#include<iostream>
#include<set>int main()
{// 1. 构造函数std::set<int>  unique_set1;int nums[] = {1, 2, 3, 4, 5, 6};std::set<int>  unique_set2(nums, nums+6);std::set<int>  unique_set3(unique_set2.begin(), unique_set2.end());// 2. 容器修改unique_set1.insert(1);unique_set1.insert(2);unique_set1.insert(3);unique_set1.emplace(4);unique_set1.emplace(5);unique_set1.erase(4);// 3. 容器查找std::cout << unique_set1.count(3) << std::endl; // 1auto item1_iter = unique_set1.find(3);std::cout << (item1_iter == unique_set1.end()) << ", " << *item1_iter << std::endl; // 0 , 3auto item2_iter = unique_set1.lower_bound(4);std::cout << (item2_iter == unique_set1.end()) << ", " << *item2_iter << std::endl; // 0 , 3auto item3_iter = unique_set1.upper_bound(5);std::cout << (item3_iter == unique_set1.end()) << ", " << *item3_iter << std::endl;// 0 , 5for(auto iter = unique_set1.begin(); iter != unique_set1.end(); iter++){std::cout << *iter << " "; // 1. 2, 3, 5}std::cout << "" << std::endl;// 4. 容器容量std::cout << unique_set1.size() << std::endl; // 4std::cout << unique_set1.empty() << std::endl; // 0unique_set1.clear();std::cout << unique_set1.size() << std::endl; // 0std::cout << unique_set1.empty() << std::endl; // 1return 0;
}

 1.2 multiset 函数

    std::multiset - cppreference.com

 1.2.1  构造函数
函数说明
set()空构造函数
 template<typename _InputIterator>	set(_InputIterator __first, _InputIterator __last)
range 构造函数
    1.2.2  容器修改
函数说明
clear()清空容器
insert插入元素
emplace插入元素,可以只传入元素类的构造函数所需参数
erase移除指定位置的元素
    1.2.3  容器查找
函数说明
count返回指定元素的个数
begin()返回首元素的 iterator
end()返回尾元素下一地址的 iterator,该 iterator 不能获取元素
find查找指定元素,若是存在返回指向该元素的 iterator;若是不存在,则返回尾 iterator
lower_boundIterator pointing to the first element that is not less than key. If no such element is found, a past-the-end iterator (see end()) is returned.
uppser_bound

Iterator pointing to the first element that is greater than key. If no such element is found, past-the-end (see end()) iterator is returned.

1.2.4  容器容量
函数说明
empty()判断 set 是否为空
size()返回 set 中的元素个数
1.2.5  示例
#include<iostream>
#include<set>int main()
{// 1. 构造函数std::multiset<int>  multi_set1;int nums1[] = {1, 2, 3, 4, 5, 6};std::multiset<int>  multi_set2(nums1, nums1+6);std::multiset<int>  multi_set3(multi_set2.begin(), multi_set2.end());// 2. 容器修改multi_set1.insert(1);multi_set1.insert(2);multi_set1.insert(3);multi_set1.insert(3);multi_set1.insert(3);multi_set1.emplace(4);multi_set1.emplace(5);multi_set1.erase(4);// 3. 容器查找std::cout << multi_set1.count(3) << std::endl; // 3auto item1_iter = multi_set1.find(3);std::cout << (item1_iter == multi_set1.end()) << ", " << *item1_iter << std::endl; // 0, 3auto item2_iter = multi_set1.lower_bound(4);std::cout << (item2_iter == multi_set1.end()) << ", " << *item2_iter << std::endl; // 0, 5auto item3_iter = multi_set1.upper_bound(4);std::cout << (item3_iter == multi_set1.end()) << ", " << *item3_iter << std::endl; // 0, 5for(auto iter = multi_set1.begin(); iter != multi_set1.end(); iter++){std::cout << *iter << " "; // 1 2 3 3 3 5}std::cout << "" << std::endl;// 4. 容器容量std::cout << multi_set1.size() << std::endl; // 6std::cout << multi_set1.empty() << std::endl; // 0multi_set1.clear();std::cout << multi_set1.size() << std::endl; // 0std::cout << multi_set1.empty() << std::endl; // 1return 0;
}

2. map/multimap

2.1 map 函数

        2.1.1  构造函数
函数说明
map默认构造函数
template< class InputIt >

map( InputIt first, InputIt last)

range 构造函数
map( std::initializer_list<value_type> init)initializer list 构造函数
        2.1.2  容器修改
函数说明
clear()清空容器
insert插入元素
emplace插入元素,可以只传入元素类的构造函数所需参数
erase移除指定位置的元素
        2.1.3  容器访问
函数说明
count返回指定 key 元素的个数
begin()返回首元素的 iterator
end()返回尾元素下一地址的 iterator,该 iterator 不能获取元素
find查找指定元素,若是存在返回指向该元素的 iterator;若是不存在,则返回尾 iterator
lower_boundIterator pointing to the first element that is not less than key. If no such element is found, a past-the-end iterator (see end()) is returned.
uppser_bound

Iterator pointing to the first element that is greater than key. If no such element is found, past-the-end (see end()) iterator is returned.

atReturns a reference to the mapped value of the element with key equivalent to key. If no such element exists, an exception of type std::out_of_range is thrown.
operator[]Returns a reference to the value that is mapped to a key equivalent to key, performing an insertion if such key does not already exist.
        2.1.4  容器容量
函数说明
empty()判断 set 是否为空
size()返回 set 中的元素个数
        2.1.5  示例
#include<iostream>
#include<map>int main()
{// 1. 构造函数std::map<int, std::string>  unique_map1;std::map<int, std::string>  unique_map2 = {{1, "a"}, {22, "bb"}, {3, "c"}};std::map<int, std::string>  unique_map3(unique_map2.begin(), unique_map2.end());// 2. 容器修改unique_map1.insert({5, "e"});unique_map1.insert({6, "f"});unique_map1.emplace(7, "g");unique_map1.emplace(8, "h");unique_map1.insert({16, "ff"});unique_map1.erase(16);// 3. 容器访问std::cout << unique_map1.count(6) << std::endl; // 1auto item_iter1 = unique_map1.find(6);std::cout << (item_iter1 == unique_map1.end())  << std::endl; // 0std::cout << item_iter1->first << ", " << item_iter1->second << std::endl; // 6, fauto item_iter2 = unique_map1.lower_bound(6);std::cout << item_iter2->first << ", " << item_iter2->second << std::endl; // 6, fauto item_iter3 = unique_map1.upper_bound(7);std::cout << item_iter3->first << ", " << item_iter3->second << std::endl; // 8, hstd::cout << unique_map1.at(7) << std::endl; // gstd::cout << unique_map1[7] << std::endl; // g// 4. 容器容量std::cout << unique_map1.empty() << std::endl; // 0std::cout << unique_map1.size() << std::endl; // 4unique_map1.clear();std::cout << unique_map1.empty() << std::endl; // 1std::cout << unique_map1.size() << std::endl; // 0    return 0;
}

 2.2 multimap 函数

         2.1.1  构造函数
函数说明
map默认构造函数
template< class InputIt >

map( InputIt first, InputIt last)

range 构造函数
map( std::initializer_list<value_type> init)initializer list 构造函数
          2.1.2  容器修改
函数说明
clear()清空容器
insert插入元素
emplace插入元素,可以只传入元素类的构造函数所需参数
erase移除指定位置的元素
        2.1.3  容器访问
函数说明
count返回指定 key 元素的个数
begin()返回首元素的 iterator
end()返回尾元素下一地址的 iterator,该 iterator 不能获取元素
find查找指定元素,若是存在返回指向该元素的 iterator;若是不存在,则返回尾 iterator
lower_boundIterator pointing to the first element that is not less than key. If no such element is found, a past-the-end iterator (see end()) is returned.
uppser_bound

Iterator pointing to the first element that is greater than key. If no such element is found, past-the-end (see end()) iterator is returned.

        2.1.4  容器容量
函数说明
empty()判断 set 是否为空
size()返回 set 中的元素个数
        2.1.5  示例
#include<iostream>
#include<map>int main()
{// 1. 构造函数std::multimap<int, std::string>  multi_map1;std::multimap<int, std::string>  multi_map2 = {{1, "a"}, {22, "bb"}, {3, "c"}};std::multimap<int, std::string>  multi_map3(multi_map2.begin(), multi_map2.end());// 2. 容器修改multi_map1.insert({5, "e"});multi_map1.insert({6, "f"});multi_map1.emplace(7, "g1");multi_map1.emplace(7, "g2");multi_map1.emplace(7, "g3");multi_map1.emplace(8, "h");multi_map1.insert({16, "ff"});multi_map1.erase(16);// 3. 容器访问std::cout << multi_map1.count(6) << std::endl; // 1auto item_iter1 = multi_map1.find(6);std::cout << (item_iter1 == multi_map1.end())  << std::endl; // 0std::cout << item_iter1->first << ", " << item_iter1->second << std::endl; // 6, fauto item_iter2 = multi_map1.lower_bound(6);std::cout << item_iter2->first << ", " << item_iter2->second << std::endl; // 6, fauto item_iter3 = multi_map1.upper_bound(7);std::cout << item_iter3->first << ", " << item_iter3->second << std::endl; // 8, h// 4. 容器容量std::cout << multi_map1.empty() << std::endl; // 0std::cout << multi_map1.size() << std::endl; // 6multi_map1.clear();std::cout << multi_map1.empty() << std::endl; // 1std::cout << multi_map1.size() << std::endl; // 0return 0;
}

四  简单实现

      1. my_set

// my_set.h#include <bits/stl_tree.h>template<typename T>
class my_set
{typedef T ValueType;typedef T KeyType;typedef std::_Rb_tree<KeyType, ValueType, std::_Identity<ValueType>, std::less<KeyType>> Rb_type;typedef typename std::_Rb_tree<T, T, std::_Identity<T>, std::less<T>>::const_iterator  const_Iterator;public:my_set(){}template<typename InputIterator>my_set(InputIterator first, InputIterator last){rb_tree._M_insert_unique(first, last);}~my_set(){}const_Iterator begin(){return rb_tree.begin();}const_Iterator end(){return rb_tree.end();}void clear(){rb_tree.clear();}void insert(ValueType& val){rb_tree._M_insert_unique(val);}void insert(ValueType&& val){rb_tree._M_insert_unique(val);}template<typename ... Args>void emplace(Args&& ... args){rb_tree._M_emplace_unique(std::forward<Args>(args)...);}template<typename ... Args>void emplace(Args& ... args){rb_tree._M_emplace_unique(std::forward<Args>(args)...);}void erase(ValueType& val){rb_tree.erase(val);}void erase(ValueType&& val){rb_tree.erase(val);}std::size_t count(ValueType& val){return rb_tree.count(val);}std::size_t count(ValueType&& val){return rb_tree.count(val);}const_Iterator find(ValueType& val){return rb_tree.find(val);}const_Iterator find(ValueType&& val){return rb_tree.find(val);}bool empty(){return rb_tree.empty();}std::size_t size(){return rb_tree.size();}private:Rb_type rb_tree;};// main.cpp
int main()
{// 1. 构造函数my_set<int>  unique_set1;int nums[] = {1, 2, 3, 4, 5, 6};my_set<int>  unique_set2(nums, nums+6);my_set<int>  unique_set3(unique_set2.begin(), unique_set2.end());// 2. 容器修改unique_set1.insert(1);unique_set1.insert(2);unique_set1.insert(3);unique_set1.emplace(4);unique_set1.emplace(5);unique_set1.erase(4);// 3. 容器查找std::cout << unique_set1.count(3) << std::endl; // 1auto item1_iter = unique_set1.find(3);std::cout << (item1_iter == unique_set1.end()) << ", " << *item1_iter << std::endl; // 0. 3for(auto iter = unique_set1.begin(); iter != unique_set1.end(); iter++){std::cout << *iter << " "; // 1 2 3 5}std::cout << "" << std::endl;// 4. 容器容量std::cout << unique_set1.size() << std::endl; // 4std::cout << unique_set1.empty() << std::endl; // 0unique_set1.clear();std::cout << unique_set1.size() << std::endl; // 0std::cout << unique_set1.empty() << std::endl; // 1return 0;
}

      2. my_multiset

// my_multiset.h#include <bits/stl_tree.h>template<typename T>
class my_multiset
{typedef T ValueType;typedef T KeyType;typedef std::_Rb_tree<KeyType, ValueType, std::_Identity<ValueType>, std::less<KeyType>> Rb_type;typedef typename std::_Rb_tree<T, T, std::_Identity<T>, std::less<T>>::const_iterator  const_Iterator;public:my_multiset(){}template<typename InputIterator>my_multiset(InputIterator first, InputIterator last){rb_tree._M_insert_equal(first, last);}~my_multiset(){}const_Iterator begin(){return rb_tree.begin();}const_Iterator end(){return rb_tree.end();}void clear(){rb_tree.clear();}void insert(ValueType& val){rb_tree._M_insert_equal(val);}void insert(ValueType&& val){rb_tree._M_insert_equal(val);}template<typename ... Args>void emplace(Args&& ... args){rb_tree._M_emplace_unique(std::forward<Args>(args)...);}template<typename ... Args>void emplace(Args& ... args){rb_tree._M_emplace_unique(std::forward<Args>(args)...);}void erase(ValueType& val){rb_tree.erase(val);}void erase(ValueType&& val){rb_tree.erase(val);}std::size_t count(ValueType& val){return rb_tree.count(val);}std::size_t count(ValueType&& val){return rb_tree.count(val);}const_Iterator find(ValueType& val){return rb_tree.find(val);}const_Iterator find(ValueType&& val){return rb_tree.find(val);}bool empty(){return rb_tree.empty();}std::size_t size(){return rb_tree.size();}private:Rb_type rb_tree;};// main.cpp
#include<iostream>
#include"my_multiset.h"int main()
{// 1. 构造函数my_multiset<int>  multi_set1;int nums[] = {1, 2, 3, 4, 5, 6};my_multiset<int>  multi_set2(nums, nums+6);my_multiset<int>  multi_set3(multi_set2.begin(), multi_set2.end());// 2. 容器修改multi_set1.insert(1);multi_set1.insert(2);multi_set1.insert(3);multi_set1.insert(3);multi_set1.insert(3);multi_set1.emplace(4);multi_set1.emplace(5);multi_set1.erase(4);// 3. 容器查找std::cout << multi_set1.count(3) << std::endl; // 1auto item1_iter = multi_set1.find(3);std::cout << (item1_iter == multi_set1.end()) << ", " << *item1_iter << std::endl; // 0, 3for(auto iter = multi_set1.begin(); iter != multi_set1.end(); iter++){std::cout << *iter << " "; // 1 2 3 3 3 5}std::cout << "" << std::endl;// 4. 容器容量std::cout << multi_set1.size() << std::endl; // 6std::cout << multi_set1.empty() << std::endl; // 0multi_set1.clear();std::cout << multi_set1.size() << std::endl; // 0std::cout << multi_set1.empty() << std::endl; // 1return 0;
}

     3. my_map

       

// my_map.h
#include <bits/stl_tree.h>
#include<initializer_list>template<typename Key, typename Value>
class my_map
{typedef std::pair<Key, Value>  ValueType;typedef std::_Rb_tree<Key, ValueType, std::_Select1st<ValueType>, std::less<Key>> Rb_type;typedef typename  Rb_type::iterator Iterator;public:my_map(){}template<typename InputIterator>my_map(InputIterator first, InputIterator last){rb_tree._M_insert_unique(first, last);}my_map(std::initializer_list<ValueType> init_list){rb_tree._M_insert_unique(init_list.begin(), init_list.end());}~my_map(){}Iterator begin(){return rb_tree.begin();}Iterator end(){return rb_tree.end();}void clear(){rb_tree.clear();}void insert(ValueType& val){rb_tree._M_insert_unique(val);}void insert(ValueType&& val){rb_tree._M_insert_unique(val);}template<typename ... Args>void emplace(Args&& ... args){rb_tree._M_emplace_unique(std::forward<Args>(args)...);}void erase(Iterator iter){rb_tree.erase(iter);}std::size_t count(Key& key){return rb_tree.count(key);}std::size_t count(Key&& key){return rb_tree.count(key);}Iterator find(Key& key){return rb_tree.find(key);}Iterator find(Key&& key){return rb_tree.find(key);}Value& at(Key& key){return (*rb_tree.lower_bound(key)).second;}Value& at(Key&& key){return (*rb_tree.lower_bound(key)).second;}Value& operator[](Key& key){return (*rb_tree.lower_bound(key)).second;}Value& operator[](Key&& key){return (*rb_tree.lower_bound(key)).second;}bool empty(){return rb_tree.empty();}std::size_t size(){return rb_tree.size();}void erase(Key& key){rb_tree.erase(key);}void erase(Key&& key){rb_tree.erase(key);}private:Rb_type rb_tree;
};// main.cppint main()
{// 1. 构造函数my_map<int, std::string>  unique_map1;my_map<int, std::string>  unique_map2 = {{1, "a"}, {22, "bb"}, {3, "c"}};my_map<int, std::string>  unique_map3(unique_map2.begin(), unique_map2.end());// 2. 容器修改unique_map1.insert({5, "e"});unique_map1.insert({6, "f"});unique_map1.emplace(7, "g");unique_map1.emplace(8, "h");unique_map1.insert({16, "ff"});unique_map1.erase(16);// 3. 容器访问std::cout << unique_map1.count(6) << std::endl; // 1auto item_iter1 = unique_map1.find(6);std::cout << (item_iter1 == unique_map1.end())  << std::endl; // 0std::cout << item_iter1->first << ", " << item_iter1->second << std::endl; // 6, fstd::cout << unique_map1.at(7) << std::endl; // gstd::cout << unique_map1[7] << std::endl; // g// 4. 容器容量std::cout << unique_map1.empty() << std::endl; // 0std::cout << unique_map1.size() << std::endl; // 4unique_map1.clear();std::cout << unique_map1.empty() << std::endl; // 1std::cout << unique_map1.size() << std::endl; // 0return 0;
}

     4. my_multimap

// my_multimap.h#include <bits/stl_tree.h>
#include<initializer_list>template<typename Key, typename Value>
class my_multimap
{typedef std::pair<Key, Value>  ValueType;typedef std::_Rb_tree<Key, ValueType, std::_Select1st<ValueType>, std::less<Key>> Rb_type;typedef typename  Rb_type::iterator Iterator;public:my_multimap(){}template<typename InputIterator>my_multimap(InputIterator first, InputIterator last){rb_tree._M_insert_equal(first, last);}my_multimap(std::initializer_list<ValueType> init_list){rb_tree._M_insert_equal(init_list.begin(), init_list.end());}~my_multimap(){}Iterator begin(){return rb_tree.begin();}Iterator end(){return rb_tree.end();}void clear(){rb_tree.clear();}void insert(ValueType& val){rb_tree._M_insert_equal(val);}void insert(ValueType&& val){rb_tree._M_insert_equal(val);}template<typename ... Args>void emplace(Args&& ... args){rb_tree._M_emplace_equal(std::forward<Args>(args)...);}void erase(Iterator iter){rb_tree.erase(iter);}std::size_t count(Key& key){return rb_tree.count(key);}std::size_t count(Key&& key){return rb_tree.count(key);}Iterator find(Key& key){return rb_tree.find(key);}Iterator find(Key&& key){return rb_tree.find(key);}bool empty(){return rb_tree.empty();}std::size_t size(){return rb_tree.size();}void erase(Key& key){rb_tree.erase(key);}void erase(Key&& key){rb_tree.erase(key);}private:Rb_type rb_tree;
};// main.cpp#include<iostream>
#include"my_multimap.h"int main()
{// 1. 构造函数my_multimap<int, std::string>  multi_map1;my_multimap<int, std::string>  multi_map2 = {{1, "a"}, {22, "bb"}, {3, "c"}};my_multimap<int, std::string>  multi_map3(multi_map2.begin(), multi_map2.end());// 2. 容器修改multi_map1.insert({5, "e"});multi_map1.insert({6, "f"});multi_map1.emplace(7, "g");multi_map1.emplace(7, "g");multi_map1.emplace(7, "g");multi_map1.emplace(8, "h");multi_map1.insert({16, "ff"});multi_map1.erase(16);// 3. 容器访问std::cout << multi_map1.count(6) << std::endl; // 1auto item_iter1 = multi_map1.find(6);std::cout << (item_iter1 == multi_map1.end())  << std::endl; // 0std::cout << item_iter1->first << ", " << item_iter1->second << std::endl; // 6, f// 4. 容器容量std::cout << multi_map1.empty() << std::endl; // 0std::cout << multi_map1.size() << std::endl; // 6multi_map1.clear();std::cout << multi_map1.empty() << std::endl; // 1std::cout << multi_map1.size() << std::endl; // 0return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/151179.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开发过程教学——交友小程序

交友小程序 1. 我的基本信息2. 我的人脉2.1 我的关注2.2 我的粉丝 3. 我的视频4. 我的相册 特别注意&#xff1a;由于小程序分包限制2M以内&#xff0c;所以要注意图片和视频的处理。 1. 我的基本信息 数据库表&#xff1a; 我的基本信息我的登录退出记录我的登录状态&#x…

运营商sdwan优缺点及sdwan服务商优势

SD-WAN(软件定义广域网)作为一种重要的网络解决方案&#xff0c;已经受到了广泛的关注和采用。然而&#xff0c; 无论是由传统运营商提供的SD-WAN还是专门的SD-WAN服务提供商&#xff0c;都存在各自的优缺点。 运营商提供的SD-WAN的缺点&#xff1a; 1. 有限的灵活性&#xf…

Kubernetes概述架构与工作流程简述

文章目录 Kubernetes概述Kubernetes优势Kubernetes 集群组件控制平面组件Node 组件 Kubernetes工作流程下期预告 Kubernetes概述 Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;可促进声明式配置和自动化。 Kubernetes 拥…

如何提升爬虫IP使用效率?精打细算的方法分享

在进行爬虫数据采集时&#xff0c;爬虫IP是不可或缺的工具。然而&#xff0c;爬虫IP的费用可能是一个爬虫项目的重要开支之一。为了帮助您节省爬虫IP经费&#xff0c;本文将分享一些经济高效的方法&#xff0c;让您在使用爬虫IP时更加节约成本&#xff0c;提高经济效益。 一、优…

gitlab登录出现的Invalid login or password问题

前提 我是在一个项目里创建的gitlab账号&#xff0c;想在别的项目里登录或者官网登录发现怎么都登陆不上 原因 在GitLab中&#xff0c;有两种不同的账号类型&#xff1a;项目账号和个人账号&#xff08;官网账号&#xff09;。 项目账号&#xff1a;项目账号是在特定GitLab…

ubuntu 设置x11vnc服务

Ubuntu 18.04 设置x11vnc服务 自带的vino-server也可以用但是不好用&#xff0c;在ubuntu论坛上看见推荐的x11vnc&#xff08;ubuntu关于vnc的帮助页面&#xff09;&#xff0c;使用设置一下&#xff0c;结果发现有一些坑需要填&#xff0c;所以写下来方便下次使用 转载请说明…

云服务仿真:完全模拟 AWS 服务的本地体验 | 开源日报 No.45

localstack/localstack Stars: 48.7k License: NOASSERTION LocalStack 是一个云服务仿真器&#xff0c;可以在您的笔记本电脑或 CI 环境中以单个容器运行。它提供了一个易于使用的测试/模拟框架&#xff0c;用于开发云应用程序。主要功能包括&#xff1a; 在本地机器上完全…

Java——StringBuffer类常用操作示例

Java——StringBuffer类常用操作 package com.yushifu.javaAPI;import java.sql.SQLOutput;//StringBuffer类&#xff08;字符串缓冲区&#xff09; //StringBuffer类与String的区别————StringBuffer的内容和长度都是可以改的 //StringBuffer类似于一个字符容器&#xff0…

通过ElementUi在Vue搭建的项目中实现CRUD

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Vue》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;这个专栏…

Linux设备驱动的精髓在哪?为何感觉写驱动就像写八股文?

Linux设备驱动的精髓在哪&#xff1f;为何感觉写驱动就像写八股文&#xff1f; 话题背景&#xff1a;随着互联网尤其是移动互联网的发展&#xff0c;Android手机操作系统得到了广泛应用&#xff0c;而Android系统是基于Linux系统开发的。另外&#xff0c;大数据、云计算等技术也…

国庆出游远程实测:ToDesk 、TeamViewer、AnyDesk远程控制软件稳定性

ToDesk 、TeamViewer、AnyDesk远程控制软件稳定性 【前言】【实测软件】【测试环境】【实操体验】1. 软件安装2. 登录速度3. 文件传输4. 操作延迟5. 画面清晰度6. 安全防护 【本文小结】 【前言】 随着科技的不断发展&#xff0c;远程控制软件已成为我们生活中不可或缺的一部分…

【UE5 Cesium】15-Cesium for Unreal 加载本地影像和地形

目录 一、加载全球无高度地形 二、加载区域DEM 三、加载离线地图影像 一、加载全球无高度地形 1. 先去如下网址下载全球无高度地形&#xff1a;Using a global terrain layer without height detail - #9 by RidhwanAziz - Cesium for Unreal - Cesium Community 下载后如下…

【Zookeeper专题】Zookeeper经典应用场景实战(一)

目录 前置知识课程内容一、Zookeeper Java客户端实战1.1 Zookeeper 原生Java客户端使用1.2 Curator开源客户端使用快速开始使用示例 二、Zookeeper在分布式命名服务中的实战2.1 分布式API目录2.2 分布式节点的命名2.3 分布式的ID生成器 三、zookeeper实现分布式队列3.1 设计思路…

一些常见分布-正态分布、对数正态分布、伽马分布、卡方分布、t分布、F分布等

目录 正态分布 对数正态分布 伽马分布 伽马函数 贝塔函数 伽马分布 卡方分布 F分布 t分布 附录 参考文献 本文主要介绍一些常见的分布&#xff0c;包括正态分布、对数正态分布、伽马分布、卡方分布、F分布、t分布。给出了分布的定义&#xff0c;推导了概率密度函数&…

中国34省区市三维地形图(直接保存)

吉林 ▼ 辽宁 ▼ 北京 ▼ 河北 ▼ 山东 ▼ 山西 ▼ 天津 ▼ 江苏 ▼ 福建 ▼ 上海 ▼ 台湾 ▼ 浙江 ▼ 广东 ▼ 广西 ▼ 海南 ▼ 香港和澳门 ▼ 安徽 ▼ 河南 ▼ 湖北 ▼ 湖南 ▼ 江西 ▼ 甘肃 ▼ 内蒙古 ▼ 宁夏 ▼ 青海 ▼ 陕西 ▼ 新疆 ▼ 贵州 …

力扣 -- 1745. 分割回文串 IV

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:bool checkPartitioning(string s) {int ns.size();vector<vector<bool>> dp(n,vector<bool>(n));for(int in-1;i>0;i--){for(int ji;j<n;j){if(s[i]s[j]){dp[i][j]i1<j?dp[i…

One Thread One Loop主从Reactor模型⾼并发服务器

One Thread One Loop主从Reactor模型⾼并发服务器 文章目录 One Thread One Loop主从Reactor模型⾼并发服务器一些补充HTTP服务器Reactor 模型eventfd通用类Any 目标功能模块划分&#xff1a;SERVER模块Buffer模块&#xff1a;编写思路&#xff1a;接口设计&#xff1a;具体实现…

【云计算网络安全】DDoS 缓解解析:DDoS 攻击缓解策略、选择最佳提供商和关键考虑因素

文章目录 一、前言二、什么是 DDoS 缓解三、DDoS 缓解阶段四、如何选择 DDoS 缓解提供商4.1 网络容量4.2 处理能力4.3 可扩展性4.4 灵活性4.5 可靠性4.6 其他考虑因素4.6.1 定价4.6.2 所专注的方向 文末送书《数据要素安全流通》本书编撰背景本书亮点本书主要内容 一、前言 云…

蓝桥杯每日一题2023.9.30

蓝桥杯大赛历届真题 - C&C 大学 B 组 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 对于此题&#xff0c;首先想到了dfs进行一一找寻&#xff0c;注意每次不要将重复的算进去&#xff0c;故我们每次循环可以记录一个开始的位置&#xff0c;下一次到这个位置时&#xff0c;…

玩转Linux—如何在Linux环境中部署MySQL、Redis和nginx

1、Linux常用命令 Linux学习之路&#xff1a; VMware虚拟机安装Linux系统(详解版) 查看当前文件目录&#xff1a;ls查看目录中文件详细信息&#xff1a;ll输出当前所处的目文件目录&#xff1a;pwdLinux查看当前IP地址&#xff1a;ifconfigWindows查看当前IP地址&#xff1…