数据结构 —— 键值对 map
目录
map的若干操作
1、emplace()
2、find(key)
3、count(key)
4、lower_bound 和 upper_bound
5、erase()
6、empty()
7、降序的map
计蒜客T3603 叫号系统
题意:
解题思路:
Code:
Leetcode1309 解码字母到整数映射
题意:
解题思路:
Code:
Leetcode205 同构字符串
题意:
解题思路:
Code:
计蒜客T1271 完美K倍子数组
题意:
解题思路:
Code:
今天主要是map专题,一般map只是一个映射,在程序中大多是一个辅助的存在。不像前面的队列和栈,它们特殊的结构有一些衍生的算法思想。而map主要是熟悉它的操作,可以更方便的查找。其实原本今天的题目和map关系不是特别大,但是写到最后一题有很多相关的操作我都很少有用过,查了很多又改了好久才模拟出来。所以在最开始先盘点一下map常用的操作~
map的若干操作
map 键值对(key - value)
1、emplace()
功能:原地构造键值对,效率更高。
示例:
myMap.emplace(2, "banana"); // 直接构造,无需创建临时对象
2、find(key)
功能:通过键查找目标,找到了就返回相应迭代器,如果没找到返回end()。
示例:
auto it = myMap.find(2);
if (it != myMap.end())cout << "找到键 " << it->first << ": " << it->second;
else cout << "未找到";
3、count(key)
功能:查找此键的个数,由于每个键在map中只能出现一次,所以只会返回1或0,可用于判断某个键是否存在。
示例:
if (myMap.count(3) > 0)cout << "键3存在";
4、lower_bound 和 upper_bound
功能:
- lower_bound:返回第一个大于等于
key
的迭代器。 upper_bound(key)
:返回第一个大于key
的迭代器。
示例:
map<int, string> myMap = {{1, "a"}, {3, "c"}, {5, "e"}};
auto low = myMap.lower_bound(3); // 指向键3
auto up = myMap.upper_bound(3); // 指向键5
5、erase()
功能:删除指定元素。
示例:
myMap.erase(key); // 按键删除
myMap.erase(2); // 删除键2
myMap.erase(myMap.begin()); // 删除第一个元素
myMap.erase(it); // 按迭代器删除
myMap.erase(first, last); // 删除区间 [first, last)
6、empty()
功能:判断此时是否为空,返回值为bool类型。
示例:
if (myMap.empty()) cout << "容器为空";
7、降序的map
功能:由于map会自动进行排序,系统默认是升序排序,其实还可以降序
示例:
map<int, int, greater<int>> myMap;myMap[3] = 30;
myMap[1] = 10;
myMap[4] = 40;
myMap[2] = 20;// 输出结果会是 4,3,2,1
for (auto i : myMap)cout << i.first << ":" << i.second << endl;
常用的函数基本就这些了,其实map的主要功能是映射,这些函数是锦上添花。下面看几道题吧~
计蒜客T3603 叫号系统
链接:叫号系统 - 计蒜客
本题很好的考察了map的基本性质,比如排序,键、值的查找等。
题意:
给定1~7七种操作,根据操作执行。
- op=1:将id和u录入系统;
- op=2:找紧急程度最小的患者,输出id并删除.如果没有输出error;
- op=3:找紧急程度最大的患者,输出id并删除.如果没有输出error;
- op=4:将编号为id的病人紧急程度改为urg;
- op=5:将紧急程度为urg的病人编号改为id;
- op=6:查找标号为id的病人的urg并输出,如果没有输出error;
- op=7:查找紧急程度为urg的病人的id并输出,如果没有输出error;
解题思路:
本题是个大模拟,要对键、值的查找比较熟悉,操作比较多需要认真一点。
接下来直接看代码吧。
Code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
#define fi first
#define se second
#define endl '\n'
const int N = 1e6+5;
map<int, int> mp;
int id,u;//Id和紧急程度
int op;
void solve()
{cin >> op;if(op==1)//将id和u录入系统{cin >> id >> u;mp[u]=id;//由于后面要找紧急程度的大小,所以按urg排序}if(op==2)//找紧急程度最小的患者,输出id并删除.如果没有输出error{if(mp.empty()){cout << "error" << endl;return ;}auto it = mp.begin();//迭代器类型索引用->cout << it->se << endl;mp.erase(it);}if(op==3)//找紧急程度最大的患者,输出id并删除.如果没有输出error{if(mp.empty()){cout << "error" << endl;return ;}auto it = --mp.end();//索引最后一个的时候用--end,end是最后一个的下一个cout << it->se << endl;mp.erase(it);}if(op==4)//将编号为id的病人紧急程度改为urg{cin >> id >> u;for(auto i:mp){if(i.se==id){auto it=mp.find(i.fi);mp.erase(it);mp[u]=id;break;}}}if(op==5)//将紧急程度为urg的病人编号改为id{cin >> id >> u;for(auto i:mp){if(i.fi==u){mp[u]=id;break;}}}if(op==6)//查找标号为id的病人的urg并输出,如果没有输出error {cin >> id;int f=0;for(auto i:mp){if(i.se==id){f=1;cout << i.fi << endl;return ;}}if(!f) cout << "error" << endl;}if(op==7)//查找紧急程度为urg的病人的id并输出,如果没有输出error {cin >> u;if(mp.find(u)==mp.end()){cout << "error" << endl;return ;}else{auto it=mp.find(u);cout << it->se << endl;}}
}
signed main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int t=1;cin >> t;while(t--) solve();return 0;
}
Leetcode1309 解码字母到整数映射
链接:1309. 解码字母到整数映射 - 力扣(LeetCode)
本题我直接用ASCII进行存值,但此题有点麻烦导致写的时候感觉有点乱
题意:
根据题中给的解码规则进行解码。
解题思路:
1~9的解码比较容易,10之后的有点麻烦,我是将数字和#分开判断,注意的是10之后一次是占据三个位置,注意下标不要超。
Code:
class Solution {
public:string freqAlphabets(string s) {map<int, int> m;string s1="";for(int i=1; i<=26; i++) m[i]='a'+i-1;//创建键值对int i;for(i=0; i<s.size()-2; i++)//如果i在后两个i+2会超{if(s[i+2]!='#') s1+=m[s[i]-'0'];else{int num=(s[i]-'0')*10+s[i+1]-'0';s1+=m[num];i+=2;}}if(i>s.size()-3)//单独处理在后两位的情况for(auto j=i; i<s.size(); i++) s1+=m[s[i]-'0'];return s1;}
};
Leetcode205 同构字符串
链接:205. 同构字符串 - 力扣(LeetCode)
题目其实还是比较简单,需要注意的点就是所有的键和值都是唯一的。
题意:
给定两个字符串看它们是否完全符合一定的映射规则。
解题思路:
按照所给字符串进行映射关系的建立和判断,如果前后冲突则为否。
Code:
class Solution {
public:bool isIsomorphic(string a, string b) {map<int, int> m;//这里用字符的ASCII码进行映射map<int, int> m1;for(int i=0; i<a.size(); i++){if(m[a[i]])//有值的话判断值{if(m[a[i]]!=b[i])return 0;}else//没值先判断当前值是否被映射过{if(m1[b[i]]) return 0;m[a[i]]=b[i],m1[i]=1;//没有就赋值,再标记一下表示被映射过}}return 1;}
};
计蒜客T1271 完美K倍子数组
链接:完美K倍子数组 - 计蒜客
这题大思路是对的,就是情况没考虑周全。
题意:
给定一个长度为n的序列,找到一个子数组使得数组中的每个元素两两之和都是k的倍数。
解题思路:
由于数组中所有的数之和都是k的倍数,那首先很容易想到如果所有数都是它的倍数那么就都可以了。由于是两两之和,所以如果K为偶数的话,如果余数为k/2的数两两相加那么也一定是k的倍数。如果前面的情况都不是,那么当两个数相加刚好等于k的倍数的时候那也可以,有点像昨天的某一道题。
Code:
unordered_map<int, int> m;//余数->出现次数
void solve()
{cin >> n >> k;for(int i=1; i<=n; i++)cin >> a[i],m[a[i]%k]++;if(k%2==0){//余数刚好是k/2/*例: 5 105 5 15 25 95*/for(int i=1; i<=n; i++) if(a[i]%k==k/2) num++;ans=max(ans,num);num=0;}for(int i=1; i<=n; i++) if(a[i]%k==0) num++;ans=max(ans,num),num=0;if(ans<2){for(int i=1; i<=n; i++) {int r = a[i]%k;//余数int c = (k-r)%k;//刚好和余数互补if(m[c]&&(m[c]>=2||(m[c]>= 1&&c!=r))){ans=2;break;}}}if(ans<2)cout << -1;else cout << ans;
}
总体来说题目依旧比较高质量,虽然没什么算法,但也暴露出基本功的问题,不涉及算法的题还要修改好久,思路不够灵敏,继续加油吧!