C++进阶---Map和Set使用及模拟实现
Posted 4nc414g0n
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++进阶---Map和Set使用及模拟实现相关的知识,希望对你有一定的参考价值。
Map和Set使用及模拟实现
1)引入
关联式容器与序列式容器:
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高
键值对:
用来表示具有一 一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息
pair
在STL中是这样为键值对定义的:
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) ;
make_pair
make_pair:参考 make_pair
template <class T1,class T2> pair<T1,T2> make_pair (T1 x, T2 y) return ( pair<T1,T2>(x,y) );
2)树形结构的关联式容器(STL分为树形结构和哈希结构)
①set
注意
注意
:
- 元素不可重复
- 在Set中
key就是value,value也是key,set中只放value,但在底层实际存放的是由<value, value>构成的键值对
- 以
RBTree
作为底层容器不允许迭代器修改Set的key/value,底层将其设置为了const(修改会造成迭代器失效)
,但是可以删除- 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则(
默认按照小于
)进行排序- set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代
- set中查找某个元素,时间复杂度为log2N
迭代器
与前面容器大同小异,参考:Set
迭代器遍历:for (std::set<int>::iterator it=myset.begin(); it!=myset.end(); ++it) std::cout << ' ' << *it;
构造 容量(略)
与前面容器大同小异,参考:Set
修改
函数 | 功能 |
---|---|
insert | 插入元素 |
erase | 删除元素 |
… | … |
关联式容器都是
使用insert/erase
进行操作而非push/pop
与前面容器大同小异,参考:Set
操作
函数 | 功能 |
---|---|
find | 获取元素的迭代器 |
count | 计算具有特定值的元素的个数 |
lower_bound | 将迭代器返回到下限 |
upper_bound | 将迭代器返回到上限 |
equal_range | 获取相等元素的范围 |
find()返回值
:如果找到元素,则返回该元素的迭代器,否则返回end()
count()
只能返回0或1
相当于库中的swap和容器swap区别:
参考
lower_bound 和 upper_bound 和 equal_range在从小到大排序数组中:
upper_bound(begin,end,num)
lower_bound(begin,end,num)
在从大到小排序数组中(重载:需要传一个比较函数):
upper_bound(begin,end,num, greater< type>())
lower_bound(begin,end,num, greater< type>())
②multiset
注意
注意:
- 与set的区别是,
multiset中的元素可以重复
,set是中value是唯一的- 其他与set一致
迭代器 构造 容量 修改(略)
参考:multiset
操作
函数 | 功能 |
---|---|
find | 拿到查找元素的迭代器 |
… | … |
由于允许重复 multiset的
find找到的是中序遍历的第一个元素
(底层为RBTree) 找到此元素以后,要继续看此元素的左孩子是不是同值元素,如果不是,就返回当前这个元素,如果是,继续取左边的同值元素,继续刚才的判断
验证
:
由于元素可以重复 这里用count可以计算个数
③map
注意
注意:
- map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素
- 在内部,map中的元素总是
按照键值key
进行比较排序的- map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)
- map支持下标访问符,即在[]中放入key,就可以找到与key对应的value
typedef pair<const key_type,mapped_type> value_type
- 底层是RBTree
构造 迭代器 容量 操作(略)
参考:map
修改
函数 | 功能 |
---|---|
insert | 插入元素 |
… | … |
pair<iterator,bool> insert (const value_type& val)
insert返回值:
- 如果插入成功,将pair<iterator,bool>中的iterator设为该插入元素的迭代器,bool设为true
- 如果插入失败说明已存在此元素,将pair<iterator,bool>中的iterator设为已存在元素的迭代器,bool设为false
参考:insert
元素访问
函数 | 功能 |
---|---|
operator[] | 访问元素 |
mapped_type& operator[] (const key_type& k) pair<iterator, bool> ret = insert(make_pair(k, mapped_type())); return ret.first->second;
使用
[]
相当于:(*((this->insert(make_pair(k,mapped_type()))).first)).second
参考insert返回值,很好理解,这里[]重载已经改变了[]的意思了
这里的mapped_type()是value的匿名对象(缺省)(相当于第一次给0
),如果value是string就会调用string的匿名对象,类比
返回值可以理解为:
- k不存在,插入默认构造函数生成缺省值的value的pair<K, V()>
- k存在,返回k对应的value值
例子:
map<string, string> dict; dict.insert(make_pair("sort", "排序")); dict["string"] = "字符串"; // 先插入("string", ""),再修改value dict["map"]; // 插入("map", "") dict["map"] = "地图,映射"; // 查找+修改
统计一个数组中每个字符串出现的次数(三种)
1.普通方法
string str[] = "sort", "sort", "tree", "insert", "sort", "tree", "sort", "test", "sort" ; map<string, int> countMap; for (auto& e : str) auto ret = countMap.find(e); if (ret != countMap.end()) //(*ret).second++; ret->second++; // ret->->second++; else countMap.insert(make_pair(e, 1)); //countMap.insert(pair<string, int>(e, 1));
2.使用insert
string str[] = "sort", "sort", "tree", "insert", "sort", "tree", "sort", "test", "sort" ; map<string, int> countMap; for (auto& e : str) //pair<map<string, int>::iterator, bool> ret = countMap.insert(make_pair(e, 1)); auto ret = countMap.insert(make_pair(e, 1)); // 插入失败,表示e对应字符串已经在map中了,++次数 if (ret.second == false) ret.first->second++;
3. 使用operator[]
string str[] = "sort", "sort", "tree", "insert", "sort", "tree", "sort", "test", "sort" ; map<string, int> countMap; for (auto& e : str) countMap[e]++;
迭代打印结果都一样
③multimap
注意
注意:
- Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对<key, value>,其中多个键值对之间的key是可以重复的
- 注意multimap没有[]重载
- 其他和map相似
3)题目
leetcode: 前K个高频单词
题目:
给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。
返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字典顺序
排序。
分析:
- 容易想到用sort库函数进行对value,也就是次数,加入一个仿函数进行排序,但值得注意的是这里要求按照字典序(相对顺序不能改变),所以不稳定排序(
sort是快排,不稳定
)一律不能用,我们只能用冒泡,归并等稳定排序
思路1:
- 写一个稳定排序(较麻烦 略)
思路2:
- 上面讲的第三种统计次数,形成一个CountMap,
- 再创建一个SortMultiMap(用multimap是因为次数会可能相同),
不用默认的less<Key>,加上greater<Key>形成逆序
,(底层由于平衡树的旋转不会改变次数相同元素的相对顺序
),- 再压入ret前k个SortMultiMap中的元素
vector<string> topKFrequent(vector<string>& words, int k) map<string, int> CountMap; for(auto& e: words) CountMap[e]++; multimap<int, string, greater<int>> SortMultiMap; for(auto& e : CountMap) SortMultiMap.insert(make_pair(e.second, e.first)); vector<string> ret; for(auto& e: SortMultiMap) if(k==0) break; ret.push_back(e.second); k--; return ret;
4)模拟实现
以上是关于C++进阶---Map和Set使用及模拟实现的主要内容,如果未能解决你的问题,请参考以下文章
C++进阶第二十篇——map和set(map和set的用法+multimap+multiset+map和set代码实现)