map暨例题
1.介绍:
映射类似于函数的x和y的关系,每一个x对应一个y,而map是每一个键对应一个值。容器中每个存储对为一个键值对,包含两个元素(键和值)。
//头文件
#include<map>
1.1map初始化:
map<int,int>mp;
或map<string,string>mp;
或map<int,string>mp;
等等
map特点:map会按照键的顺序从小到大自动排序,键的类型可以比大小。
1.2常用的函数:
代码 | 含义 | 复杂度 |
mp.find(key) | 返回键为key的映射的迭代器 | O(logN) |
mp.erase(it) | 删除迭代器对应的键和值 | O(logN) |
mp.erase(first,last) | 删除左闭右开区间迭代器对应的键和值 | O(last−first) |
mp.size() | 返回映射的对数 | O(1) |
mp.clear() | 清空map中的所有元素 | O(N) |
mp.insert() | 插入元素,插入时要构造键值对 | O(logN) |
mp.empty() | 如果map为空,返回true,否则返回false | O(1) |
mp.begin() | 返回指向map第一个元素的迭代器(地址) | O(1) |
mp.end() | 返回指向map尾部的迭代器(最后一个元素的下一个地址) | O(1) |
mp.rbegin() | 返回指向map最后一个元素的迭代器(地址) | O(1) |
mp.rend() | 返回指向map第一个元素前面(上一个)的逆向迭代器(地址) | O(1) |
mp.count(key) | 查看元素是否存在,因为map中键是唯一的,所以存在返回1,不存在返回0 | O(logN) |
mp.lower_bound() | 返回一个迭代器,指向键>= key的第一个元素 | |
mp.upper_bound() | 返回一个迭代器,指向键> key的第一个元素 |
1.3 迭代器进行正向遍历:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
#define int long long
signed main()
{
map<int,int> mp;
mp[3] = 4;
mp[5] = 6;
mp[7] = 8;
map<int,int>::iterator it = mp.begin();
while(it != mp.end()) {cout << it->first << " " << it->second << "\n";it ++;
}return 0;
}
1.4二分查找:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+5;
signed main()
{//lower_bound()//返回一个迭代器,指向键>=key的第一个元素 //upper_bound() //返回一个迭代器,指向键>key的第一个元素 map<int, int> m;m[3]=2,m[6]=2,m[2]=2,m[8]=5,m[9]=4;
//由于 map 会自动排序,最终 m 中的键值对顺序是
// 2: 2
// 3: 2
// 6: 2
// 8: 5
// 9: 4map<int, int>::iterator it1 = m.lower_bound(2);cout << it1->first << "\n";//it1->first=2map<int, int>::iterator it2 = m.upper_bound(2);cout << it2->first << "\n";//it2->first=3return 0;
}
添加元素:
//先声明
map<string, string> mp;
方式一
mp["学习"] = "写作业";
mp["玩耍"] = "打go";
方式二:插入元素构造键值对
mp.insert(make_pair("vegetable","蔬菜"));
2.访问元素:
2.1下标访问
mp["娃娃菜"] = "娃哈哈";
cout << mp["娃娃菜"] << "\n";//只是简写的一个例子,程序并不完整
2.2遍历访问:
迭代器访问:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+5;
signed main()
{
map<string,string>mp;
mp["小明"]="小红",mp["小美"]="小帅";
map<string,string>::iterator it;
for(it = mp.begin(); it != mp.end(); it++) {
// 键 值
// it是结构体指针访问所以要用 -> 访问
cout << it->first << " " << it->second << "\n";
//*it是结构体变量 访问要用 . 访问
//cout<<(*it).first<<" "<<(*it).second;
}
return 0;
}
3.例题
3.1题目链接:205. 同构字符串 - 力扣(LeetCode)
题目描述:
给定两个字符串 s
和 t
,判断它们是否是同构的。
如果 s
中的字符可以按某种映射关系替换得到 t
,那么这两个字符串是同构的。
每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。
示例 1:
输入:s = "egg", t = "add"输出:true示例 2:
输入:s = "foo", t = "bar"输出:false示例 3:
输入:s = "paper", t = "title"输出:true
提示:
1 <= s.length <= 5 * 10000
t.length == s.length
s
和t
由任意有效的 ASCII 字符组成
解题思路:
首先需要我们判断 s 和 t 每个位置上的字符是否都一一对应,即 s 的任意一个字符被 t 中唯一的字符对应,同时 t 的任意一个字符被 s 中唯一的字符对应。这也被称为「双射」的关系。
以示例 2 为例,t 中的字符 a 和 r 虽然有唯一的映射 o,但对于 s 中的字符 o 来说其存在两个映射 {a,r},故不满足条件。
因此,我们需要开两个哈希表,第一张哈希表以s中的字符串为键,映射至t中的字符串为值,第二张哈希表以t中的字符串为键,映射至s中的字符串为值。
从左往右遍历字符串,并不断更新哈希表,如果存在冲突(即当前下标对应的字符s[index]已经存在映射且不为t[inedex]或者t[index]不为s[inedex])时,说明两个字符无法构成同构,返回false,如果没有冲突返回true即可。
运行代码:
class Solution {
public:bool isIsomorphic(string s, string t) {unordered_map<char, char> s2t;unordered_map<char, char> t2s;int len = s.length();for (int i = 0; i < len; ++i) {char x = s[i], y = t[i];if ((s2t.count(x) && s2t[x] != y) || (t2s.count(y) && t2s[y] != x)) {return false;}s2t[x] = y;t2s[y] = x;}return true;}
};
3.2题目链接:完美K倍子数组 - 计蒜客
题目描述:
解题思路:
1.若K是奇数,则只有这个新数组中全是K的倍数的数,才是完美的
2.若K是偶数,则除了新的数组全是K的倍数情况,还有一种是任意两个数的余数的和是K/2的倍数。
3.如果这个数组中没有K的倍数的数,则考虑两个数之和是K,而这种情况下,数组长度只能是2.
运行代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
#define int long long
signed main()
{int n,k,x;cin>>n>>k;map<int,int>mp;int bs=0,half=0,ff=0;
/*bs:记录数组中能被 k 整除的数的个数。这些数与其他任何数的和都能被 k 整除。
half:记录余数为 k/2 的数的个数。如果 k 是偶数,这些数两两之和能被 k 整除。
ff:如果存在两个数的余数互补(a%k + b%k = k),则标记为 1。*/for(int i=1; i<=n; i++) {cin >> x;if(x % k == 0)bs++; // 统计能被 k 整除的数的个数if(x % k == k/2)half++; // 统计余数为 k/2 的数的个数if(mp[k - x%k] == 0) // 检查是否存在互补余数mp[x%k]++;elseff = 1; // 存在互补余数对
}if(k%2==1){half=0; //奇数 k 时,余数为 k/2 无意义}int an=max(half,bs); // 取两种可能的最大值if(an>1) // 至少有 2 个数满足条件cout<<an;else if(ff)cout<<2; // 存在互补余数对,子数组长度为 2elsecout<<-1;return 0;
}
3.3题目链接:叫号系统 - 计蒜客
题目描述:
有一家医院,病人到医院看病时,会先挂号排队,此时病人会获得一个唯一的编号 id。医院有一个病情评测机,它会自动根据病情给病人评测出病情的紧急程度 urg。一个病人到达医院后,会自动挂号、评测病情的紧急程度,并且会将信息加入到医院的叫号系统。叫号系统内保证所有排队的病人中所有的 id 和紧急程度 urg 均不相同。
样例输入1:
7
1 2 3
1 3 4
6 3
7 3
4 3 5
2
3
样例输出1:
4
2
2
3
运行代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
#define int long long // 定义宏,将int替换为long long类型signed main() {int n, op, id, urg;map<int, int> a, b; // 定义两个map容器:// a: id映射到urg(病人ID到紧急程度)// b: urg映射到id(紧急程度到病人ID)cin >> n; // 输入操作总数while(n--) { // 处理每个操作cin >> op; // 输入操作类型// 操作1:添加病人if(op == 1) {cin >> id >> urg; // 输入病人ID和紧急程度a[id] = urg; // 建立id到urg的映射b[urg] = id; // 建立urg到id的映射}// 操作2:叫号(选择紧急程度最小的病人)else if(op == 2) {if(b.size() == 0) { // 如果没有病人cout << "error\n";} else {// 输出紧急程度最小的病人的ID(b.begin()是最小的urg)cout << b.begin()->second << '\n';// 删除该病人的记录a.erase(b.begin()->second); // 从a中删除b.erase(b.begin()); // 从b中删除}}// 操作3:叫号(选择紧急程度最大的病人)else if(op == 3) {if(a.size() == 0) { // 如果没有病人cout << "error\n";} else {// 输出紧急程度最大的病人的ID(--b.end()是最大的urg)cout << (--b.end())->second << '\n';// 删除该病人的记录a.erase((--b.end())->second); // 从a中删除b.erase(--b.end()); // 从b中删除}}// 操作4:修改病人紧急程度else if(op == 4) {cin >> id >> urg; // 输入病人ID和新的紧急程度b.erase(a[id]); // 删除旧的urg到id映射a[id] = urg; // 更新id到urg的映射b[urg] = id; // 建立新的urg到id映射}// 操作5:修改病人IDelse if(op == 5) {cin >> id >> urg; // 输入新的ID和紧急程度a.erase(b[urg]); // 删除旧的id映射a[id] = urg; // 建立新的id到urg映射b[urg] = id; // 更新urg到id映射}// 操作6:根据ID查询紧急程度else if(op == 6) {cin >> id; // 输入要查询的病人IDif(a.find(id) == a.end()) { // 如果ID不存在cout << "error\n";} else {cout << a[id] << '\n'; // 输出对应的紧急程度}}// 操作7:根据紧急程度查询IDelse if(op == 7) {cin >> urg; // 输入要查询的紧急程度if(b.find(urg) == b.end()) { // 如果紧急程度不存在cout << "error\n";} else {cout << b[urg] << '\n'; // 输出对应的病人ID}}}return 0;
}