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_bound | Iterator 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_bound | Iterator 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_bound | Iterator 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. |
| at | Returns 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_bound | Iterator 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;
}
相关文章:
C++ -- 学习系列 关联式容器 set 与 map
一 关联式容器是什么? c 中有两种容器类型:关联式容器与序列式容器(顺序容器) 关联式中的容器是按照关键字来存储与访问的,序列式容器(顺序容器)则是元素在容器中的相对位置来存储与访问的。…...
Day 04 python学习笔记
Python数据容器 元组 元组的声明 变量名称(元素1,元素2,元素3,元素4…….) (元素类型可以不同) eg: tuple_01 ("hello", 1, 2,-20,[11,22,33]) print(type(tuple_01))结果&#x…...
Moonbeam Ignite强势回归
参与Moonbeam上最新的流动性计划 还记得新一轮的流动性激励计划吗?Moonbeam Ignite社区活动带着超过300万枚GLMR奖励来啦!体验新项目,顺便薅一把GLMR羊毛。 本次Moonbeam Ignite活动的参与项目均为第二批Moonbeam生态系统Grant资助提案中获…...
【改造后序遍历算法】95. 不同的二叉搜索树 II
95. 不同的二叉搜索树 II 解题思路 遍历每一个节点查看以k为根节点的二叉搜索树储存所有左子树的根节点储存所有右子树的根节点将左子树和右子树组装起来 将根节点储存在向量中 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeN…...
栈的基本操作(数据结构)
顺序栈的基本操作 #include <stdlib.h> #include <iostream> #include <stdio.h> #define MaxSize 10typedef struct{int data[MaxSize];int top; }SqStack;//初始化栈 void InitStack(SqStack &S){S.top -1; } //判断栈空 bool StackEmpty(SqStack S)…...
D. Jellyfish and Mex Codeforces Round 901 (Div. 2)
Problem - D - Codeforces 题目大意:有一个n个数的数组a,数m初始为0,每次操作可以删除任意一个数,然后m加上那个数,求n次操作和m的最小值 1<n<5000;0<a[i]<1e9 思路:可以发现&am…...
操作系统内存管理相关
1. 虚拟内存 1.1 什么是虚拟内存 虚拟内存是计算机系统内存管理的一种技术,我们可以手动设置自己电脑的虚拟内存。不要单纯认为虚拟内存只是“使用硬盘空间来扩展内存“的技术。虚拟内存的重要意义是它定义了一个连续的虚拟地址空间,并且 把内存扩展到硬…...
Sui流动性质押黑客松获胜者公布,助力资产再流通
Sui流动质押黑客松于日前结束Demo Day演示,其中有五个团队获奖、六个团队荣誉提名,共有超过30个项目获得参赛资格。此外,有两个团队赢得了Sui上DeFi协议提供的赏金。 本次黑客松的目的是挖掘并奖励将流动质押功能集成到其apps和产品中的开发…...
为什么在使用PageHelper插件时,指定的每页记录数大小失效?显示所有的记录数
1.问题现象: 这里指定每页显示5条,却把所有的记录数都显示出来了 2.分析: 之前是可以的,然后发现:PageHelper.startPage(pageNum,pageSize) 和执行sql的语句 顺序颠倒了,然后就出错了。 3.验证…...
XML文档基础
什么是XML XML (eXtensible Markup Language,可扩展标记语言) 是一种用于存储和传输数据的文本文件格式。用户可以按照XML规则自定义标记,XML 的设计目标是传输数据,而不是显示数据,因此它是一种通用的标记语言,可用于…...
软考知识汇总-软件工程
软件工程 1 能力成熟度模型(CMM)2 能力成熟度模型集成(CMMI)2.1阶段式模型2.2 连续式模型 3 软件过程模型 1 能力成熟度模型(CMM) 将软件工程成熟度分为5个级别 初始级:杂乱无章,很…...
力扣:119. 杨辉三角 II(Python3)
题目: 给定一个非负索引 rowIndex,返回「杨辉三角」的第 rowIndex 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 来源:力扣(LeetCode) 链接:力扣(LeetCode)…...
指针笔试题(带解析版)
题目2: struct MyStruct {int num;char* pcname;short sdate;char cha[2];short sba[4]; }*p; //结构体大小为32字节 //p0x100000 int main() {p 0x100000;printf("%p\n", p 0x1);//p:结构体指针,1下一个结构体指针,…...
服务器搭建(TCP套接字)-libevent版(服务端)
Libevent 是一个开源的事件驱动库,用于开发高性能、并发的网络应用程序。它提供了跨平台的事件处理和网络编程功能,具有高性能、可扩展性和可移植性。下面详细讲解 Libevent 的主要组成部分和使用方法。 一、事件基础结构(event_base&#x…...
斐波那契模型系列【动态规划】
动态规划步骤 1、状态表示 是什么:dp表(可能是一维或二维数组)里的值所表示的含义。 怎么来: 1、题目要求 2、经验题目要求 3、发现重复子问题 2、状态转移方程 dp[i]... 3、初始化 保证填表不越界 4、填表顺序 5、返回值 写代码时…...
【Java】微服务——Nacos注册中心
目录 1.Nacos快速入门1.1.服务注册到nacos1)引入依赖2)配置nacos地址3)重启 2.服务分级存储模型2.1.给user-service配置集群2.2.同集群优先的负载均衡 3.权重配置4.环境隔离4.1.创建namespace4.2.给微服务配置namespace 5.Nacos与Eureka的区别…...
Redis Cluster Gossip Protocol: PING, PONG, MEET
返回目录 PING / PONG / MEET 的发送 过程 计算freshNodes。freshNodes表示在消息中能携带的,在cluster节点字典中的节点总数,但需要减去myself和对端节点,因为myself的信息会存储在消息头中。实际上,并非所有在cluster节点字典…...
httpserver 下载服务器demo 以及libevent版本的 httpserver
实现效果如下: 图片可以直接显示 cpp h 这些可以直接显示 其他的 则是提示是否要下载 单线程 还有bug 代码如下 先放上来 #include "httpserver.h" #include "stdio.h" #include <stdlib.h> #include <arpa/inet.h> #include…...
构建强大的RESTful API:@RestController与@Controller的对比与应用
构建强大的RESTful API:RestController与Controller的对比与应用 前言什么是RESTful APIRestController,Controller,ResponseBody1. Controller注解:2. RestController注解:3. ResponseBody注解: 示例非thy…...
【Java-LangChain:使用 ChatGPT API 搭建系统-10】评估(下)-当不存在一个简单的正确答案时
第十章,评估(下)-当不存在一个简单的正确答案时 在上一章中,了解了如何评估 LLM 模型在 有明确正确答案 的情况下的输出,我们可以编写一个函数来判断 LLM 输出是否正确地分类并列出产品。 然而,如果 LLM …...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
