数据结构和算法之map/set
map和set都是STL关联容器的一种,一般当用来快速查找一个元素是否存在或者是否重复,可以通过所谓的哈希集合和哈希映射来解决。除了熟悉各自的底层原理之外,也要会灵活的使用各个容器。
map
几个需要熟记的点
- 通过key-value实现映射功能,底层实现是红黑树,而key是有序的,并且不可重复的,查询和增删的效率都是o(logn)。
使用
按照程序来介绍
//包含头文件
#include<map>
//创建+初始化
map<type(key),type(value)>mymap;
map<int,string>mymap{{1,"1"}}
map<int,string>newmymap(mymap);
map<int,string>newmymap(++mymap.begin(),mymap.end());
//迭代器
//用法:++p,p++,--p,p--,*p,迭代器之间只能==和!=进行比较
for(auto iter=mymap.begin();iter!=mymap.end();iter++){
cout<<iter->first<<iter->second<<endl;
插入元素
insert和[];[]:若 map 容器内部没有存储以 [ ] 运算符内指定数据为键的键值对,则使用 [ ] 运算符会向当前 map 容器中添加一个新的键值对。insert先来看下源码。
其中value_type为pair类型的数据。
pair<iterator,bool>insert(const value_type&x){return.. }
iterator insert (const_iterator position, const value_type& val)
..
int main() {
map<int, string>mymap{ {1,"1"} };
pair<int, string>value(2, "2");
pair<int, string>value1(0, "5");
mymap.insert(value);
mymap.insert(++mymap.begin(), value1);
mymap[3] = "3";
for (auto iter = mymap.begin(); iter != mymap.end(); iter++) {
cout << iter->first << ":" << iter->second << endl;
}
return 0;
}
查找一个元素
常用find和count.find(key):在map容器中查找键值为key的键值对,成功找到返回指向该键值对的双向迭代器,反之,返回和end()一样的迭代器。
count:查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。
//如果想查找一个key是否存在,一般有两种写法:
if (hash.find(key) != hash.end()),说明key存在
if (hash.count(key) != 0)
int main() {
map<int, string>mymap{ {1,"1"} };
pair<int, string>value(2, "2");
pair<int, string>value1(0, "5");
mymap.insert(value);
mymap.insert(++mymap.begin(), value1);
mymap[3] = "3";
mymap[1] = "a";
for (auto iter = mymap.begin(); iter != mymap.end(); iter++) {
cout << iter->first << ":" << iter->second << endl;
}
int a=mymap.count(1);
cout << a;
auto it = mymap.find(1);
if (it != mymap.end()) {
cout << "存在"<<endl;
}
auto it1 = mymap.find(6);
if (it1 != mymap.end()) {
cout << "key不存在不会输出这部分";
}
if (mymap.count(1) != 0) {
cout << "存在"<<endl;
}
return 0;
}
修改value
利用mymap.[key]:如果key不存在就会将key添加进去
利用mymap.at(key):会检测key,不存在就会抛出异常
删除键值对
迭代器失效问题
C++11
(1)
iterator erase (const_iterator position);
(2)
size_type erase (const key_type& k);
(3)
iterator erase (const_iterator first, const_iterator last);
int main() {
map<int, string>mymap{ {1,"1"} };
pair<int, string>value(2, "2");
pair<int, string>value1(0, "5");
mymap.insert(value);
mymap.insert(++mymap.begin(), value1);
mymap[3] = "3";
mymap[1] = "a";
for (auto iter = mymap.begin(); iter != mymap.end(); iter++) {
cout << iter->first << ":" << iter->second << endl;
}
mymap.erase(++mymap.begin());
mymap.erase(2);
for (auto iter = mymap.begin(); iter != mymap.end(); iter++) {
cout << iter->first << ":" << iter->second << endl;
}
mymap.erase(mymap.begin(), mymap.end());
for (auto iter = mymap.begin(); iter != mymap.end(); iter++) {
cout << iter->first << ":" << iter->second << endl;
}
return 0;
}
其他
//统计key出现的频率
static bool cmp(const pair<int, int>&a, const pair<int, int>&b) {
return a.second > b.second;// 按照频率从大到小排序
}
int main() {
map<int, int>myhash;
//统计value出现的频率
vector<int>ans{ 1,2,3,1,2,3,1,2,3,1,2,2 ,5,6};
for (int i = 0; i < ans.size(); i++) {
myhash[ans[i]]++;
}
cout << myhash[1] << endl;
//找出value最大的,善于利用pair
int m = INT_MIN;
for (auto iter = myhash.begin(); iter != myhash.end(); iter++) {
if (iter->second > m) {
m = iter->second;
}
}
cout << m<<endl;
//对value排序,要先转成vector,自定义排序方式
vector<pair<int, int>>vec(myhash.begin(), myhash.end());
sort(vec.begin(), vec.end(), cmp);
for (auto iter = vec.begin(); iter != vec.end(); iter++) {
cout << iter->first ;
}
cout << endl;
return 0;
}
unordered_map
- 同样通过key-value实现映射功能,不过底层实现是散列表,通过将关键码映射到hash表中的一个位置来访问记录,查找o(1),海量数据处理经常使用。元素的排序是无序的,key也不可重复。(LRU哈希链表就可以实现有序)
set
- key和value包装在一起,key就是value。底层也是红黑树,也是key有序,查询效率也是o(logn)和map不同,因为不是键值对的形式,所以也就没有[]这个插入方式,另外,map是key不可重复,set是值不可重复(key-value合一)。
unordered_set
- 底层实现是哈希表,元素是无序的,查询和增删效率是O(1)。
经典题型: