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

news2024/11/20 3:28:24

一   关联式容器是什么?

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>::template
        rebind<_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>::template
	rebind<_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;

    .....
     iterator
      insert(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 requirements
      typedef 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)
#endif

    public:
      // 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>::template
	rebind<_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;

  ......

      iterator
      insert(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>::template
	rebind<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>::template
	rebind<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;

   ......
    iterator
      insert(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; // 1
    auto item1_iter = unique_set1.find(3);

    std::cout << (item1_iter == unique_set1.end()) << ", " << *item1_iter << std::endl; // 0 , 3

    auto item2_iter = unique_set1.lower_bound(4);
    std::cout << (item2_iter == unique_set1.end()) << ", " << *item2_iter << std::endl; // 0 , 3

    auto item3_iter = unique_set1.upper_bound(5);
    std::cout << (item3_iter == unique_set1.end()) << ", " << *item3_iter << std::endl;
    // 0 , 5
    for(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; // 4
    std::cout << unique_set1.empty() << std::endl; // 0

    unique_set1.clear();

    std::cout << unique_set1.size() << std::endl; // 0
    std::cout << unique_set1.empty() << std::endl; // 1

    return 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; // 3
    auto item1_iter = multi_set1.find(3);

    std::cout << (item1_iter == multi_set1.end()) << ", " << *item1_iter << std::endl; // 0, 3

    auto item2_iter = multi_set1.lower_bound(4);
    std::cout << (item2_iter == multi_set1.end()) << ", " << *item2_iter << std::endl; // 0, 5

    auto item3_iter = multi_set1.upper_bound(4);
    std::cout << (item3_iter == multi_set1.end()) << ", " << *item3_iter << std::endl; // 0, 5

    for(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; // 6
    std::cout << multi_set1.empty() << std::endl; // 0

    multi_set1.clear();

    std::cout << multi_set1.size() << std::endl; // 0
    std::cout << multi_set1.empty() << std::endl; // 1
    
    return 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; // 1
    auto item_iter1 = unique_map1.find(6);
    std::cout << (item_iter1 == unique_map1.end())  << std::endl; // 0
    std::cout << item_iter1->first << ", " << item_iter1->second << std::endl; // 6, f
    auto item_iter2 = unique_map1.lower_bound(6);
    std::cout << item_iter2->first << ", " << item_iter2->second << std::endl; // 6, f
    auto item_iter3 = unique_map1.upper_bound(7);

    std::cout << item_iter3->first << ", " << item_iter3->second << std::endl; // 8, h

    std::cout << unique_map1.at(7) << std::endl; // g
    std::cout << unique_map1[7] << std::endl; // g

    // 4. 容器容量
     std::cout << unique_map1.empty() << std::endl; // 0
     std::cout << unique_map1.size() << std::endl; // 4

     unique_map1.clear();
     std::cout << unique_map1.empty() << std::endl; // 1
     std::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; // 1
     auto item_iter1 = multi_map1.find(6);
     std::cout << (item_iter1 == multi_map1.end())  << std::endl; // 0
     std::cout << item_iter1->first << ", " << item_iter1->second << std::endl; // 6, f
     auto item_iter2 = multi_map1.lower_bound(6);
     std::cout << item_iter2->first << ", " << item_iter2->second << std::endl; // 6, f
     auto 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; // 0
      std::cout << multi_map1.size() << std::endl; // 6

      multi_map1.clear();
      std::cout << multi_map1.empty() << std::endl; // 1
      std::cout << multi_map1.size() << std::endl; // 0

      return 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; // 1
    auto item1_iter = unique_set1.find(3);

    std::cout << (item1_iter == unique_set1.end()) << ", " << *item1_iter << std::endl; // 0. 3

    for(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; // 4
    std::cout << unique_set1.empty() << std::endl; // 0

    unique_set1.clear();

    std::cout << unique_set1.size() << std::endl; // 0
    std::cout << unique_set1.empty() << std::endl; // 1
    return 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; // 1
    auto item1_iter = multi_set1.find(3);

    std::cout << (item1_iter == multi_set1.end()) << ", " << *item1_iter << std::endl; // 0, 3

    for(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; // 6
    std::cout << multi_set1.empty() << std::endl; // 0

    multi_set1.clear();

    std::cout << multi_set1.size() << std::endl; // 0
    std::cout << multi_set1.empty() << std::endl; // 1
    return 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.cpp

int 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; // 1
    auto item_iter1 = unique_map1.find(6);
    std::cout << (item_iter1 == unique_map1.end())  << std::endl; // 0
    std::cout << item_iter1->first << ", " << item_iter1->second << std::endl; // 6, f

    std::cout << unique_map1.at(7) << std::endl; // g
    std::cout << unique_map1[7] << std::endl; // g

    // 4. 容器容量
     std::cout << unique_map1.empty() << std::endl; // 0
     std::cout << unique_map1.size() << std::endl; // 4

     unique_map1.clear();
     std::cout << unique_map1.empty() << std::endl; // 1
     std::cout << unique_map1.size() << std::endl; // 0
    return 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; // 1
     auto item_iter1 = multi_map1.find(6);
     std::cout << (item_iter1 == multi_map1.end())  << std::endl; // 0
     std::cout << item_iter1->first << ", " << item_iter1->second << std::endl; // 6, f

     // 4. 容器容量
      std::cout << multi_map1.empty() << std::endl; // 0
      std::cout << multi_map1.size() << std::endl; // 6

      multi_map1.clear();
      std::cout << multi_map1.empty() << std::endl; // 1
      std::cout << multi_map1.size() << std::endl; // 0
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++标准模板(STL)- 类型支持 ()

对象、引用、函数&#xff08;包括函数模板特化&#xff09;和表达式具有称为类型的性质&#xff0c;它限制了对这些实体所容许的操作&#xff0c;并给原本寻常的位序列提供了语义含义。 附加性基本类型及宏 实现定义的空指针常量 NULL 定义于头文件 <clocale> 定义于…

2.类与对象 拜访对象村

2.1 椅子大战 在图形接口画出正方形、圆形与三角形。当用户选点图形时&#xff0c;图形需要顺时针转360并依据形状的不同播放播放不同的AIF文件。胜者可以坐上名贵宝椅。 面向过程&#xff1a; rotate(shapeNum) {//旋转360 } playSound(shapeNum) {//查询播放哪个AIF文件//播…

嵌入式Linux应用开发-驱动大全-同步与互斥②

嵌入式Linux应用开发-驱动大全-同步与互斥② 第一章 同步与互斥②1.3 原子操作的实现原理与使用1.3.1 原子变量的内核操作函数1.3.2 原子变量的内核实现1.3.2.1 ATOMIC_OP在 UP系统中的实现1.3.2.2 ATOMIC_OP在 SMP系统中的实现 1.3.3 原子变量使用案例1.3.4 原子位介绍1.3.4.1…

pyqt5使用经验总结

pyqt5环境配置注意&#xff1a; 安装pyqt5 pip install PyQt5 pyqt5-tools 环境变量-创建变量名&#xff1a; 健名&#xff1a;QT_QPA_PLATFORM_PLUGIN_PATH 值为&#xff1a;Lib\site-packages\PyQt5\Qt\plugins pyqt5经验2&#xff1a; 使用designer.exe进行设计&#xff1…

Maven - MacOS 快速安装

配置信息 Maven 版本&#xff1a;3.6.3Maven 地址&#xff1a;Index of /dist/maven/maven-3IDEA&#xff1a;2023 Tips&#xff1a;Maven 版本最好不要超过 3.8.0&#xff0c;最新版 Maven 会不兼容一些配置信息。上面的 Maven 地址里可以选择自己想下载的版本&#xff08;这…

【源码】hamcrest 源码阅读及空对象模式、模板方法模式的应用

文章目录 前言1. 类图概览2. 源码阅读2.1 抽象类 BaseMatcher2.1 接口 Description提炼模式&#xff1a;空对象模式 2. 接口 Description 与 SelfDescribing 配合使用提炼模式 模板方法 后记 前言 hamcrest &#xff0c;一个被多个测试框架依赖的包。听说 hamcrest 的源码质量…

【maven】idea中基于maven-webapp骨架创建的web.xml问题

IDEA中基于maven-webapp骨架创建的web工程&#xff0c;默认的web.xml是这样的。 <!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name…

go语法入门2

字符串 使用双引号或反引号引起来的任意个字符。它是字面常量。 func main() {var a "abc\n测试" // \n换行fmt.Println(a) } abc 测试func main() {var a "abc\n\t测试" \\换行后在tabfmt.Println(a) } abc测试func main() {var a abc测试 …

CppCheck静态代码检查工具教程【Windows和Linux端】

目录 1、背景 2、特性介绍 2.1、检查结果 2.2、检查范围 2.3、支持的检查规则&#xff08;列举一些&#xff09;: 2.4、自定义规则 3、linux 端 4、windows 端 1、背景 最近调研了几款 c/c 代码静态检查工具&#xff0c;包括 cppcheck、cpplint、cppdepend、splint、ts…

cmip6数据处理之降尺度

专题一 CMIP6中的模式比较计划 1.1 GCM介绍全球气候模型&#xff08;Global Climate Model, GCM&#xff09;&#xff0c;也被称为全球环流模型或全球大气模型&#xff0c;是一种用于模拟地球的气候系统的数值模型。这种模型使用一系列的数学公式来描述气候系统的主要组成部分…

分享Arduino环境下加速下载 第三方库或芯片包

Content 问题描述问题解决 问题描述 众所周知&#xff0c;由于网络的问题&#xff0c;导致Arduino里面的包下载速度非常慢&#xff0c;甚至下了非常久&#xff0c;最后也还是出现下载失败的情况。 有的人打开了加速器&#xff0c;但是也依旧是速度非常慢&#xff0c;为什么呢…

OpenGLES:绘制一个混色旋转的3D圆柱

一.概述 上一篇博文讲解了怎么绘制一个混色旋转的立方体 这一篇讲解怎么绘制一个混色旋转的圆柱 圆柱的顶点创建主要基于2D圆进行扩展&#xff0c;与立方体没有相似之处 圆柱绘制的关键点就是将圆柱拆解成&#xff1a;两个Z坐标不为0的圆 一个长方形的圆柱面 绘制2D圆的…

【列表渲染+收集表单数据+过滤器+内置指令+自定义指令】

列表渲染收集表单数据过滤器内置指令自定义指令 1 列表渲染1.1 基本列表1.2 key的作用与原理1.3 列表过滤1.4 列表排序1.5 Vue监测数据改变的原理 2 收集表单数据3 过滤器4 内置指令4.1 v-text指令4.2 v-html指令4.3 v-cloak指令4.4 v-once指令4.5 v-pre指令 5 自定义指令 1 列…

【网络安全---sql注入(2)】如何通过SQL注入getshell?如何通过SQL注入读取文件或者数据库数据?一篇文章告诉你过程和原理。

前言 本篇博客主要是通过piakchu靶场来讲解如何通过SQL注入漏洞来写入文件&#xff0c;读取文件。通过SQL输入来注入木马来getshell等&#xff0c;讲解了比较详细的过程&#xff1b; 如果想要学习SQL注入原理以及如何进行SQL注入&#xff0c;我也写了一篇详细的SQL注入方法及…

毅速3D打印:深骨位零件制造首选3D打印

在模具制造领域&#xff0c;深骨位零件由于其复杂形状和结构&#xff0c;传统的加工方法往往难以满足生产要求&#xff0c;导致产品不良问题频繁出现。而如今&#xff0c;随着3D打印技术的普及&#xff0c;深骨位零件在3D打印面前变得不再困难。 3D打印是一种快速成型技术&…

2120 -- 预警系统题解

Description OiersOiers 国的预警系统是一棵树&#xff0c;树中有 &#xfffd;n 个结点&#xff0c;编号 1∼&#xfffd;1∼n&#xff0c;树中每条边的长度均为 11。预警系统中只有一个预警信号发射站&#xff0c;就是树的根结点 11 号结点&#xff0c;其它 &#xfffd;−1…

10.1 调试事件读取寄存器

当读者需要获取到特定进程内的寄存器信息时&#xff0c;则需要在上述代码中进行完善&#xff0c;首先需要编写CREATE_PROCESS_DEBUG_EVENT事件&#xff0c;程序被首次加载进入内存时会被触发此事件&#xff0c;在该事件内首先我们通过lpStartAddress属性获取到当前程序的入口地…

向日葵todesk使用遇到问题

1 设置向日葵自启动 查找向日葵安装位置 dpkg -L sunloginclient 在启动程序中加入向日葵 2 解决正在进入桌面 一直无法进入界面 sudo apt-get update sudo apt-get upgrade sudo apt-get install lightdm # 最后一个执行过程中选择lightdm 3 todesk不能正常启动 sudo sys…

第10章 MySQL(一)

10.1 谈谈MySQL的架构 难度:★★ 重点:★ 白话解析 要想彻底的理解MySQL,它的架构一定要先弄清楚,当Java程序员通过JDBC或者Mybatis去执行一条SQL的时候,到底经历了什么。下边先看一幅图: 户端:Java程序员通过JDBC或者Mybatis去拿MySQL的驱动程序,实际上就是拿客户端。…