目录
[4、lower_bound 和 upper_bound](#4、lower_bound 和 upper_bound)
[计蒜客T3603 叫号系统](#计蒜客T3603 叫号系统)
[Leetcode1309 解码字母到整数映射](#Leetcode1309 解码字母到整数映射)
[Leetcode205 同构字符串](#Leetcode205 同构字符串)
[计蒜客T1271 完美K倍子数组](#计蒜客T1271 完美K倍子数组)
今天主要是map专题,一般map只是一个映射,在程序中大多是一个辅助的存在。不像前面的队列和栈,它们特殊的结构有一些衍生的算法思想。而map主要是熟悉它的操作,可以更方便的查找。其实原本今天的题目和map关系不是特别大,但是写到最后一题有很多相关的操作我都很少有用过,查了很多又改了好久才模拟出来。所以在最开始先盘点一下map常用的操作~
map的若干操作
map 键值对(key - value)
1、emplace()
功能 :原地构造键值对,效率更高。
示例:
cpp
myMap.emplace(2, "banana"); // 直接构造,无需创建临时对象
2、find(key)
**功能:**通过键查找目标,找到了就返回相应迭代器,如果没找到返回end()。
示例:
cpp
auto it = myMap.find(2);
if (it != myMap.end())
cout << "找到键 " << it->first << ": " << it->second;
else cout << "未找到";
3、count(key)
**功能:**查找此键的个数,由于每个键在map中只能出现一次,所以只会返回1或0,可用于判断某个键是否存在。
示例:
cpp
if (myMap.count(3) > 0)
cout << "键3存在";
4、lower_bound 和 upper_bound
功能:
- lower_bound:返回第一个大于等于
key
的迭代器。 upper_bound(key)
:返回第一个大于key
的迭代器。
示例:
cpp
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()
功能:删除指定元素。
示例:
cpp
myMap.erase(key); // 按键删除
myMap.erase(2); // 删除键2
myMap.erase(myMap.begin()); // 删除第一个元素
myMap.erase(it); // 按迭代器删除
myMap.erase(first, last); // 删除区间 [first, last)
6、empty()
**功能:**判断此时是否为空,返回值为bool类型。
示例:
cpp
if (myMap.empty()) cout << "容器为空";
7、降序的 map
**功能:**由于map会自动进行排序,系统默认是升序排序,其实还可以降序
示例:
cpp
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:
cpp
#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:
cpp
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 同构字符串
题目其实还是比较简单,需要注意的点就是所有的键和值都是唯一的。
题意:
给定两个字符串看它们是否完全符合一定的映射规则。
解题思路:
按照所给字符串进行映射关系的建立和判断,如果前后冲突则为否。
Code:
cpp
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倍子数组
这题大思路是对的,就是情况没考虑周全。
题意:
给定一个长度为n的序列,找到一个子数组使得数组中的每个元素两两之和都是k的倍数。
解题思路:
由于数组中所有的数之和都是k的倍数,那首先很容易想到如果所有数都是它的倍数 那么就都可以了。由于是两两之和,所以如果K为偶数 的话,如果余数为k/2的数两两相加 那么也一定是k的倍数。如果前面的情况都不是,那么当两个数相加刚好等于k的倍数的时候那也可以,有点像昨天的某一道题。
Code:
cpp
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 10
5 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;
}
总体来说题目依旧比较高质量,虽然没什么算法,但也暴露出基本功的问题,不涉及算法的题还要修改好久,思路不够灵敏,继续加油吧!