wordpress中文问答模块如何优化seo技巧
♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥
♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥
♥♥♥我们一起努力成为更好的自己~♥♥♥
♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥
♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥
✨✨✨✨✨✨ 个人主页✨✨✨✨✨✨
前面我们已经学习了一种新的容器map和set,这一篇博客我们来看看如何使用map和set在我们的算法题目中大放光彩,准备好了吗~我们发车去探索C++的奥秘啦~🚗🚗🚗🚗🚗🚗
目录
仿函数
前面K个高频单词
解法一:仿函数+排序
解法二:map和multimap的极致使用
单词识别
随机链表的复制
仿函数
在正式开始题目练习之前,我们首先来看看仿函数的概念,在后面我们会进行使用~
仿函数(Functor)是C++中的一种设计模式,通过将函数功能封装在类中并重载
operator()
实现。它使得类对象可以像函数一样被调用,兼具函数的灵活性与类的封装性。仿函数能够包含状态信息,在多次调用间保持特定状态,适用于需要自定义行为或优化性能的场合。本质上仿函数是实现了一个重载了[ ]的类~
我们可以来看看下面的代码:
//定义一个仿函数,实现两个整数加法
class Add
{
public:int operator()(int a, int b){return a + b;}
};int main()
{Add add;cout << add(4, 6) << endl;return 0;
}
有了这样一个仿函数,我们就可以像函数调用一样,使用一个类对象,通过仿函数,可以定义自定义的比较、排序、查找等行为~
再比如下面的代码(我们希望使用算法库里面的sort来实现降序,但是算法库默认是升序的,这个时候我们就可以提供仿函数)
#include<algorithm>
//定义一个仿函数,进行两个整型的比较
class Compare
{
public:bool operator()(int a, int b){return a > b;//我们希望是降序,前面的大于后面的}
};int main()
{vector<int> v = { 1,6,2,7,9,0,3,5 };//算法库里面的sort默认是升序,我们可以写一个仿函数处理成降序sort(v.begin(), v.end(), Compare());//这里的Compare()相当于一个匿名对象for (auto e : v){cout << e << " ";}return 0;
}
sort
在排序过程中多次调用匿名对象的operator()
方法来比较元素,这样比较算法我们就可以自己定义,更加灵活~
前面K个高频单词
前面K个高频单词
解法一:仿函数+排序
这个与我们前面的单词计数类似,但是需要注意的是这里想要的是降序,所以就需要使用到我们的仿函数~更多的注意点我们在代码中进行注释~
class Solution
{
public:class Compare{public:bool operator()(const pair<string, int>& a, const pair<string, int>& b){解决方案一:控制比较逻辑return (a.second > b.second) || (a.second == b.second && a.first < b.first);//个数相同让字典序小的在前面//解决方案二:使用稳定排序,不改变比较逻辑//return a.second > b.second;//比较个数int}};vector<string> topKFrequent(vector<string>& words, int k) {map<string, int> mp;for (auto e : words){mp[e]++;}//sort要求随机迭代器,而map是双向迭代器//所以需要导入vector中——支持随机迭代器vector<pair<string, int>> v(mp.begin(), mp.end());//迭代器区间初始化//默认是升序,我们希望是降序,并且希望按照second去排序//提供仿函数//sort为不稳定排序,会更改顺序与以前的一致性//解决方案一:控制比较逻辑sort(v.begin(), v.end(), Compare());//解决方案二:使用稳定排序//stable_sort(v.begin(),v.end(),Compare());//(stable_sort底层是归并排序)vector<string> ret;//找前面k个进行插入for (int i = 0; i < k; i++){ret.push_back(v[i].first);}return ret;}
};
这里特别注意的是sort是不稳定排序(底层是快速排序),当两个元素的值相等时,排序后它们的相对顺序可能会发生改变,最开始map排序好的字典序又会发生变化~
这里有两种解决方案:
1、控制排序逻辑(当个数相等时,让字典序小的在前面)
class Compare { public:bool operator()(const pair<string, int>& a, const pair<string, int>& b){解决方案一:控制比较逻辑return (a.second > b.second) || (a.second == b.second && a.first < b.first);//个数相同让字典序小的在前面} };
2、不对排序逻辑进行修改,使用稳定的排序,库里面提供了stable_sort可以帮助我们实现,底层事实上是归并排序~
解法二:map和multimap的极致使用
如果并不想写仿函数,有没有什么方法呢?也是有的,我们可以再使用一个multimap来进行对个数进行排序,需要注意的是这里不可以使用map了,因为不同字符串个数可能相等,那么map不支持key冗余就麻烦了~
class Solution
{
public:vector<string> topKFrequent(vector<string>& words, int k) {//map统计个数map<string, int> count_map;for (auto e : words){count_map[e]++;}//multimap对个数进行排序//修改第三个参数,我们希望按照降序排序multimap<int, string, greater<int>> sort_map;//写法一auto it = count_map.begin();while (it != count_map.end()){sort_map.insert({ it->second,it->first });it++;}//写法二/*for(auto e: count_map){sort_map.insert({e.second,e.first});}*/vector<string> ret;//不可以使用前面的it,类型是不一样的auto itt = sort_map.begin();//取前面k个while (k--){ret.push_back(itt->second);itt++;}return ret;}
};
注意点:
1、使用multimap的时候,我们希望的是大的在前面,第三个参数需要修改为greater<int>
2、这里不会出现字典序问题,是因为如果key已经存在,新元素会被插入到所有具有相同键的元素的末尾(或根据比较器定义的顺序),就顺便解决了字典序问题~
单词识别
单词识别
这个题目就比较温柔了,也就是我们前面的单词统计多了一些要求,我们知道使用
cin
或scanf
来读取输入时,它们默认会在遇到空格、制表符或换行符时停止读取。这意味着如果你直接读取一个字符串,它只会读取到第一个空格之前的内容。为了读取包含空格的整行字符串,我们可以使用getline
函数,getline
会读取一整行,包括空格,直到遇到换行符为止。
我们这里也可以不使用getline,直接使用while循环读取一个单词就进行处理!!!
#include <iostream>
using namespace std;
#include<map>int main()
{map<string,int> count_map;string s;while(cin>>s){int n=s.size();for(int i=0;i<n;i++){//处理大小写if(s[i]>='A'&&s[i]<='Z')s[i]+=32;//处理句号if(s[i]=='.')s.erase(i,1);//删除句号}count_map[s]++;}for(auto& e:count_map){cout<<e.first<<":"<<e.second<<endl;}
}
顺利通过,这一个题目还是比较简单的~
随机链表的复制
随机链表的复制
看这题目长度就知道这不是一个简单题,我们前面使用C语言写,写了一堆,具体可以看看这一篇博客题目练习之链表那些事儿(再续)
接下来我们一起来探索C++有什么奇妙的地方呢~~~
既然是是map的奇妙使用,那么一定需要就是map了,思路和前面差不多,不过有了map,代码实现就会更加简单了~
思路:
- 第一遍遍历:
- 创建一个
map
来存储原节点和复制节点的映射关系。- 遍历原链表,为每个节点创建一个新的复制节点,并将其连接到复制链表中。
- 将原节点和复制节点的映射关系存储到
map
中。- 第二遍遍历:
- 重新遍历原链表,根据
map
中的映射关系设置复制节点的random
指针。- 返回结果:
- 返回复制链表的头节点。
//随机链表的复制
/*
// Definition for a Node.
class Node {
public:int val;Node* next;Node* random;Node(int _val) {val = _val;next = NULL;random = NULL;}
};
*/class Solution
{
public:Node* copyRandomList(Node* head){map<Node*, Node*> CopyMap;// 用于存储原节点和复制节点的映射关系Node* newHead = nullptr;Node* newTail = nullptr;Node* cur = head;// 第一遍遍历:创建复制节点并构建映射关系while (cur){// 如果复制链表为空,初始化头和尾指针if (newHead == nullptr){newHead = newTail = new Node(cur->val);}// 否则,创建新节点并连接到复制链表的尾部else{newTail->next = new Node(cur->val);newTail = newTail->next;}// 将原节点和复制节点的映射关系存储到map中CopyMap[cur] = newTail;//继续往后面遍历cur = cur->next;}// 第二遍遍历:设置复制节点的random指针//重新遍历原来的链表cur = head;//复制链表的当前结点Node* copycur = newHead;while (cur){// 如果原节点的random指针为空,复制节点的random指针也为空if (cur->random == nullptr)copycur->random = nullptr;// 否则,根据映射关系设置复制节点的random指针elsecopycur->random = CopyMap[cur->random];cur = cur->next;copycur = copycur->next;}return newHead;//返回新链表头结点}
};
成功通过,为了更好地理解,我们举例说明:
假设原链表中有两个节点
X
和Y
,其中X.random -> Y
。在复制链表中,我们希望对应的两个节点x'
和y'
也有x'.random -> y'
。
- 在第一遍遍历中,我们创建了
x'
和y'
,并将X
映射到x'
,Y
映射到y'
存储在nodeMap
中。- 在第二遍遍历中,当处理节点
X
时,cur->random
是Y
,我们通过nodeMap[Y]
找到y'
,然后将x'.random
设置为y'
。这样,我们就正确地设置了复制链表中节点的
random
指针~
有了map我们就不需要手动去进行链接,方便了不少~
♥♥♥本篇博客内容结束,期待与各位优秀程序员交流,有什么问题请私信♥♥♥
♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥
✨✨✨✨✨✨个人主页✨✨✨✨✨✨