目录
set
介绍
迭代器
使用
set
find
count
lower_bound
使用
upper_bound
使用
multiset
介绍
find
count
equal_range
编辑
oj题
两个数组的交集
思路
差集思路
代码
map
介绍
元素类型 -- pair
使用
insert
编辑
传参
显式构建对象
匿名对象
make_pair
隐式类型转换 -- c++11新特性
无法插入相同key值
返回值
[ ]
应用
原理
其他接口
oj题
前k个高频单词
编辑
思路
sort:
静态成员函数
仿函数
map版本:
代码
sort版本:
静态成员
仿函数
map版本:
括号匹配
思路
代码
单词识别
思路
代码
复杂链表的复制
思路
代码
是标准库中的一个容器,它提供了一种有序的、不重复的数据存储方式
基于红黑树实现,是key版本的搜索树(所以需要传入比较方式)
迭代器
不允许修改值,所以它的两种迭代器其实都是const迭代器
find
如果没有找到,会返回它的end位置的迭代器
set
s; s.insert(1); s.insert(1); s.insert(7); s.insert(2); s.insert(1); s.insert(3); for (auto c : s) { cout << c << " "; } cout << endl; if (s.find(3)!=s.end()) { //可以用判断是否找到 cout << "success" << endl; }
count
返回该值在结构中出现的次数
但由于set不允许有重复元素,所以count返回的值要么是1,要么是0
所以可以用它来实现find的功能,判断某个元素在不在
if (s.count(3)) { cout << "success" << endl; }
lower_bound
它返回一个[>=当前值]的迭代器
如果没找到,就返回end位置的迭代器
使用
可以用来找到某个数字存在的左边界
图上代码中,itlow指向30
upper_bound
和上面的功能类似,但是是返回比当前值大的迭代器
如果没找到,就返回end位置的迭代器
使用
图上代码中,itup指向70(中序序列中第一个比60大的数字)
- 这个性质可以完美的应用在erase中,它传入的范围是左闭右开
- 所以可以删除某段区间上的所有数字 / 重复数字
- 但去重这个特性在set中没啥用.本来set就自带去重,但在multiset中很有用
介绍
- 是标准模板库中的一个容器类,它是一个有序的关联容器,允许存储多个具有相同值的元素
- 和set高度相似,唯一不同的是它可以存储重复值
它就在set下:
find
返回中序序列中第一个该数字
void test2() { multiset
s; s.insert(7); s.insert(1); s.insert(7); s.insert(7); s.insert(5); s.insert(3); for (auto c : s) { cout << c << " "; } cout << endl; auto it = s.find(7); while ( it != s.end()) { //从返回的7后继续打印 cout << *it << " "; ++it; } cout << endl; }
count
在multiset中,count才算是真正发挥了用武之地,可以用来统计某重复数字的出现次数
void test2() { multiset
s; s.insert(7); s.insert(1); s.insert(7); s.insert(7); s.insert(5); s.insert(3); cout << s.count(7) << endl; }
equal_range
可以找到某重复数字的区间范围,在multiset里才有用
typedef std::multiset
::iterator It; void test3() { multiset s; s.insert(7); s.insert(1); s.insert(7); s.insert(7); s.insert(5); s.insert(3); std::pair ret = s.equal_range(7); //相当于将7这个数字的左右区间(右区间是开的)给了ret s.erase(ret.first, ret.second); for (auto tmp : s) { cout << tmp << " "; } }
两个数组的交集
349. 两个数组的交集 - 力扣(LeetCode)
反正,无论找交集还是找差集,无序状态绝对是最难找的
- 所以,首先要排序
- 要注意 -- 重复元素不能算在交集里!!(看示例1中的结果)
- 所以,可以考虑放在set里,自动排序+去重
- 然后,从头遍历两个数组,如果两个值一大一小,那小的就不可能是交集中的元素(因为是有序的嗷),所以小的指针++
- 就这么一直遍历就行,直到一方完毕
差集思路
- 还是一样的,先排序!!很重要
- 然后因为是有序的,所以小的肯定是差集中的元素(因为此时已经不相等了,那大的之后的元素更大,更不可能相等了,所以肯定是差集)
- 然后小的指针++
vector intersection(vector& nums1, vector& nums2) {
vector ans;
set n1,n2;
for(const auto& tmp:nums1){
n1.insert(tmp);
}
for(const auto& tmp:nums2){
n2.insert(tmp);
}
auto i=n1.begin(),j=n2.begin();
while(i!=n1.end()&&j!=n2.end()){
if(*i<*j){
++i;
}
else if(*j<*i){
++j;
}
else{
ans.push_back(*i);
++i;
++j;
}
}
return ans;
}
- 是一个关联容器,它提供了 键-值 对(也就是key_value)的存储和检索功能
- 在插入新键值对时,它会自动进行排序,以保持键的有序性(内部使用自平衡的二叉搜索树(通常是红黑树)来实现)
元素类型 -- pair
insert
insert有多种传参方式,要么传迭代器,要么传值
要注意!!!这里的值是pair类型的嗷!!!所以我们要传入pair类型的
传参
有多种方式可以传入pair对象
显式构建对象
map
res; pair tmp("sort", "排序"); res.insert(tmp); 匿名对象
res.insert(pair
("apple", "苹果")); make_pair
可以使用这个模板函数,构建需要的pair对象
res.insert(make_pair("one","一"));
隐式类型转换 -- c++11新特性
res.insert({ "banana", "香蕉" });
注意,这里是多参数的隐式类型转换,在c++11中才被允许使用
而且因为是多参数,需要带上{ }
无法插入相同key值
map
res; pair tmp("sort", "排序"); res.insert(tmp); res.insert(pair ("apple", "苹果")); res.insert({ "banana", "香蕉" }); res.insert(make_pair("one","一")); res.insert(make_pair("one", "xxxx")); //插入相同key for (const auto& t : res) { cout << t.first << ":" << t.second << endl; } 不会插入,不会覆盖掉原value值
返回值
它返回的是一个pair对象,且second成员可以用来判断传入key是否存在
first成员是插入元素的迭代器
[ ]
这个重载用的很多很多
注意看这个函数原型,传入key值,返回它对应的value值的引用
也就是说,可以通过这个接口,访问到特定key值的value / 修改它的value
void test1() { map
res; pair tmp("sort", "排序"); res.insert(tmp); res.insert(pair ("apple", "苹果")); res.insert({ "banana", "香蕉" }); res.insert(make_pair("one","一")); for (const auto& t : res) { cout << t.first << ":" << t.second << endl; } cout << endl; res["apple"] = "xxx"; for (const auto& t : res) { cout << t.first << ":" << t.second << endl; } }
应用
还记得我们之前在set的模拟实现中,有对传入的对象记过数字吗
- 当时需要手动判断+插入/修改次数
- 但在map中,有了[ ]重载,就不需要那么麻烦
void test2() { string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" }; map
ans; for (auto e : arr){ ans[e]++; } for (const auto& t : ans){ cout << t.first << ":" << t.second << endl; } }
- 短短的一行代码,就可以完成插入+修改次数的功能
原理
- 是因为[ ]的底层实际上是基于insert设计的
- 它传入的key要是不存在,就insert,value值用传入类型的默认构造初始化
- key要是存在,就返回一个pair对象,first成员就是该元素的迭代器
- 然后,两种情况都会返回元素的value值的引用
大概就是这样的一个函数
其他接口
和set基本差不多,但就是要注意一点,记得map里元素类型是pair
692. 前K个高频单词 - 力扣(LeetCode)
- 首先利用map的特性,统计出每个单词的出现次数
- 然后!!要注意,他还有要求 -- 相同次数的按照字典序排列
- 所以,要不然就是sort,要不然就是依次放进两个map里(map的排序具有稳定性)
sort:
自己传入比较方法
静态成员函数
- 因为sort自身的比较函数实际上是没有this指针的,它只有两个参数
- 但如果我们在类内定义一个函数作为其比较函数,实际上会有三个参数(看下面的介绍 -- 它的比较函数不允许修改参数)
- 所以要定义成静态函数(直接可以用类名调用)
仿函数
map版本:
- 先让这些字符串按照字典序排列
- (为了让按次数排序之后,相同次数的还能按字典序排列)
- 如果先按次数排,那第二次排序的时候,就会让字典序的先排前面,而不会优先排列次数多的
- 特别注意的是:第二次排序的时候,一定要使用multimap!!!不然次数一样的进不去!!!
static bool compare(const pair& x,const pair& y){
if(x.second>y.second){
return true;
}
else if(y.second==x.second&&x.first topKFrequent(vector& words, int k) {
map com;
vector ans;
for(auto tmp:words){
++com[tmp];
}
vector> tmp(com.begin(),com.end());
sort(tmp.begin(),tmp.end(),compare);
for(auto it=tmp.begin();itfirst);
}
return ans;
}
struct compare{
bool operator()(const pair& x,const pair& y){
if(x.second>y.second){
return true;
}
else if(y.second==x.second&&x.first topKFrequent(vector& words, int k) {
map com;
vector ans;
for(auto tmp:words){
++com[tmp];
}
vector> tmp(com.begin(),com.end());
sort(tmp.begin(),tmp.end(),compare());
for(auto it=tmp.begin();itfirst);
}
return ans;
}
vector topKFrequent(vector& words, int k) {
map com1;
for(const auto& tmp:words){
++com1[tmp];
}
multimap> com2; //注意是multi版本的,不然次数一样的插入不进去
for(auto& tmp:com1){
com2.insert(make_pair(tmp.second,tmp.first));
}
auto it=com2.begin();
vector ans;
while(k--){
ans.push_back(it->second);
++it;
}
return ans;
}
20. 有效的括号 - 力扣(LeetCode)
之前有写过这道题,但当时使用c手搓了个栈做的,巨麻烦
但是c++中,有自带的栈,就会好一点
但除此之外,map在这道题里也很好用
- 将左括号和右括号关联起来
- 然后遍历传入的字符串,左括号入栈,右括号匹配
bool isValid(string s) {
map com;
stack st;
com['(']=')';
com['[']=']';
com['{']='}';
for(const auto& tmp:s){
if(com.count(tmp)==1){
st.push(tmp); //入左括号
}
else{
if(st.empty()){
return false;
}
char top=st.top();
st.pop();
if(com[top]!=tmp){ //不匹配
return false;
}
}
}
if(!st.empty()){
return false;
}
return true;
}
单词识别_牛客题霸_牛客网 (nowcoder.com)
很基础的一道题,就是需要处理一下输入
- 标准输入会将空格视作结束符,所以可以使用cin作为输入函数
- 还要注意,最后一个单词和' . '之间没有空格,所以需要注意最后标点的处理
- 还要记得全部转成小写字符
最后,排一下序就行
#include
#include
#include
#include
复杂链表的复制
剑指 Offer 35. 复杂链表的复制 - 力扣(LeetCode)
首先先复制一条链表出来,主要要处理的就是random指针
- 重点就是要把原结点和拷贝结点关联在一起
- 构建拷贝结点的random,就直接利用原结点的random -> 找到此时的random拷贝结点,链接上就行
Node* copyRandomList(Node* head) {
map bri;
Node* copyhead=nullptr,*copytail=nullptr,*cur=head,*node=nullptr;
while(cur){ //构建新链表+关联结点
node=new Node(cur->val);
if(copyhead==nullptr){
copyhead=copytail=node;
}
else{
copytail->next=node;
copytail=node;
}
bri[cur]=node;
cur=cur->next;
}
node=head;copytail=copyhead;
while(node){
if(node->random==nullptr){ //注意要特殊判断一下
copytail->random=nullptr;
}
else{ //直接指就行
copytail->random=bri[node->random];
}
node=node->next;
copytail=copytail->next;
}
return copyhead;
}