C++map和set
目录
1.关联式和序列式容器
2.键值对
3.树形结构的关联式容器
3.1 set
3.1.1 set的构造
3.1.2 set的修改
3.2 multiset
3.3 map
3.3.1 map的构造
3.3.2 map的插入操作
3.3.3 map的迭代器访问
3.3.4 operator[] :给一个key返回对应value的引用
3.4 multimap:允许冗余
1.关联式和序列式容器
- STL中的部分容器如:string、vector、list、deque、array、forward_list等,这 些容器统称为序列式容器,因为逻辑结构为线性序列的数据结构,两个位置存储的值之间⼀般没有紧密的关联关系,比如交换⼀下,他依旧是序列式容器。顺序容器中的元素是按他们在容器中的存储位置来顺序保存和访问的。
- 关联式容器也是用来存储数据的,与序列式容器不同的是,关联式容器逻辑结构通常是非线性结构, 两个位置有紧密的关联关系,交换⼀下,他的存储结构就被破坏了。顺序容器中的元素是按关键字来保存和访问的。关联式容器有map/set系列和unordered_map/unordered_set系列。其中存储的是<key,value>结构的键值对,在数据检索时比序列式容器效率更高。
2.键值对
用来表示一种一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
键值对的定义:
template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(): first(T1()), second(T2())
{}pair(const T1& a, const T2& b): first(a), second(b)
{}
};
3.树形结构的关联式容器
根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。
3.1 set
- set是按照一定次序存储元素的容器
- 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。 set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
- 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
- set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对 子集进行直接迭代。
- set在底层是用二叉搜索树(红黑树)实现的。
注意:
- 与map/multimap不同,map/multimap中存储的是真正的键值对,set中只放 value,但在底层实际存放的是由构成的键值对。
- set中插入元素时,只需要插入value即可,不需要构造键值对。
- set中的元素不可以重复(因此可以使用set进行去重)。
- 使用set的迭代器遍历set中的元素,可以得到有序序列
- set中的元素默认按照小于来比较
- set中查找某个元素,时间复杂度为:$log_2 n$
- set中的元素不允许修改->原因:底层使用二叉搜索树进行存储,如果元素修改那么二叉树的结构会被破坏
- set中的底层使用二叉搜索树(红黑树)来实现。
3.1.1 set的构造
函数声明 | 功能介绍 |
set (const Compare& comp = Compare(), const Allocator& = Allocator() ); | 构造空的set |
set (InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator() ); | 用[first, last)区 间中的元素构造 set |
set ( const set<key,compare,allocator>& x); | set的拷贝构造 |
3.1.2 set的修改
函数声明 | 功能介绍 |
pair<iterator,bool> insert ( const value_type& x ) | 在set中插入元素x,实际插入的是构成的键值对,如果插入成功,返回<该元素在set中的 位置,true>,如果插入失败,说明x在set中已经存在,返回<在set中的位置,false> |
void erase ( iterator first, iterator last ) | 删除set中[first, last)区间中的元素 |
3.2 multiset
- multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。
- 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是组成 的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器 中进行修改(因为元素总是const的),但可以从容器中插入或删除。
- 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则 进行排序。
- multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭 代器遍历时会得到一个有序序列。
- multiset底层结构为二叉搜索树(红黑树)。
注意:与set的区别就是multiset中的元素可以重复。
3.3 map
3.3.1 map的构造
map<T,V> mp;
//T,V都表示类型
3.3.2 map的插入操作
1.有名对象
map<string,int> m; pair<string,int> p("apple",1); m.insert(p);
2.匿名对象
map<string,int> m; m.insert(pair<string,int>("apple",1));
3.make_pair
map<string,int> m; m.insert(make_pair("apple",1));
4.多参数隐式类型转换
map<string,int> m; m.insert({"apple",1});
3.3.3 map的迭代器访问
map<string, string>::iterator it = dict.begin(); auto it = dict.begin(); while (it != dict.end()) {//cout << (*it).first <<" "<< (*it).second << endl;cout << it->first << " " << it->second << endl;//cout << it.operator->()->first << " " << it.operator->()->second << endl;// ++it; }
注意:insert看key,若key存在则无论后序value怎么改变都不改变。(有则插入失败,无则插入成功)
3.3.4 operator[] :给一个key返回对应value的引用
V& operator[](const K& key) {pair<iterator, bool> ret = insert({ key, V() });return ret.first->second; }
1.pair<iterator, bool> ret = insert({ key, V() });
- iterator:始终指向key部分(新插入的key或与key相等)
- bool:成功 true,失败 false
- V():缺省值
2.ret.first->second
存在两个pair——>1.insert返回值pair 2.节点KV pair
过程:
- ret.first——>取pair中的迭代器
- ->second——>结点指针里面的pair取value
代码示例:统计次数
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };map<string, int> countMap;for (auto& e : arr){auto it = countMap.find(e);if (it == countMap.end()){countMap.insert({ e, 1 });}else{it->second++;}countMap[e]++;}map<string, int> countMap;for (auto& e : arr){countMap[e]++;}
[]相关操作:
map<string, string> dict;// 插入dict["left"];// 插入+修改dict["right"] = "右边";// 修改dict["left"] = "左边";// 查找cout << dict["left"] << endl;
3.4 multimap:允许冗余
注意:multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以 重复的。