前言:菜鸟写博客给自己看,我是菜鸟。
1:随机链表的复制
1.1题目要求:
1.2解题思路:
可以分两步来实现代码:
①先将示例1链表中的val值以及next的指向关系深拷贝到另一个新的链表当中
②再处理新链表中,random的指向关系。
思考:
第①步是很容易实现的,那么如何处理新链表中random的指向关系呢?这时候可以试着用map去解决。
map<key,T>:
map<key,T>的底层是红黑树,其内部的数据是使用pair<key,T>存储键值对数据(这里的key,T是数据类型),键值对是一一对应/映射的关系。举一个简单的例子:
"苹果" <-> "apple"
"橘子" <-> "orange"
其中,key是不可修改的,而T是可修改的。
分析:
在第①步创建新节点的同时,将新节点与旧节点通过map<Node*,Node*> 建立键值对的关系,这样就得到了新旧节点间的映射关系。
更进一步的,再仔细想想如何处理新链表中random的指向关系呢?
是 新节点->random = 旧节点->random 吗? 显然不是,题目要求构造一个全新的链表,这样的指向关系,会使得新旧链表的random指向同一个节点。
因此不是 新节点->random = 旧节点->random ,而是 新节点->random = 旧节点->random所映射的节点,可以通过map函数中的operator[]来实现。
总结:
在第①步创建链表的同时,将新旧节点通过map<Node*,Node*>建立映射关系。
在第②步中,可以通过映射关系,例如 旧13节点的random 指向 7节点,那么 新13节点 可以通过 旧13节点的random 先找到 旧7节点,再根据 map<Node*,Node*> 建立的映射关系,找到 新7节点 ,从而建立正确的指向关系
1.3实现代码:
/*
// 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* copyhead = nullptr;
Node* copytail = nullptr;
Node* cur = head;
while(cur)
{
if(copyhead == nullptr)
{
copyhead = copytail = new Node(cur->val);
}
else
{
copytail->next = new Node(cur->val);
copytail = copytail->next;
}
copymap[cur] = copytail;
cur = cur->next;
}
Node* copy = copyhead;
cur = head;
while(cur)
{
if(cur->random == nullptr)
{
copy->random = nullptr;
}
else
{
copy->random = copymap[cur->random];
}
cur = cur->next;
copy = copy->next;
}
return copyhead;
}
};
2:前K个高频单词
2.1题目要求:
前言:这是一道综合应用stl的题目,难度对于新人(博主)来说有点大,本题涉及一些对先前知识的查漏补缺。
2.2解题思路:
思路:
👉:首先应该想到的是:创建一个map<string,int> CountMap 来统计每个单词出现的个数。
👉:map类会自动将数据按照 key(前一个数据类型)来排列,即此时 CountMap 中的数据是按照 string(对应首字母的ascii码值)来排序。
👉:但是题目要求是出现次数多的在前,因此我们需要对map中的数据做处理
👉:将map中的数据拷贝到vector中,并排列得到我们想要的顺序
👉:最后再输出vector中前k个字母。
注:上述还涉及很多细节问题,在2.3中再做讨论。
2.3实现代码:
class Solution {
public:
struct compare
{
bool operator()(const pair<string,int>& x1,const pair<string,int>& x2)
{
return x1.second > x2.second || (x1.second == x2.second && x1.first < x2.first);
}
};
vector<string> topKFrequent(vector<string>& words, int k) {
//统计每个字母出现的次数
map<string,int> CountMap;
for(auto& e : words)
{
CountMap[e]++;
}
//将map中的数据拷贝到vector中进行比较
vector<pair<string,int>> v(CountMap.begin(),CountMap.end());//按字符串顺序排列
//默认的比较方式不满足我们的要求,因此需要自己写一个仿函数来实现
stable_sort(v.begin(),v.end(),compare());
//取前K个单词,并存放到 str 中作为返回值
vector<string> str;
for(int i = 0; i<k; i++)
{
str.push_back(v[i].first);
}
return str;
}
};
将代码逐一拆分解释:
map<string,int> CountMap; for(auto& e : words) { CountMap[e]++; }
该段代码用于统计每个字母出现的次数,通过map类(map<string,int>)来记录单词与出现字数之间的映射关系。
注:map<key,T> map类会按照key默认进行排序
vector<pair<string,int>> v(CountMap.begin(),CountMap.end());//按字符串顺序排列 stable_sort(v.begin(),v.end(),compare());
因为map类的默认排序不满足题给要求,因此我们需要新创建一个vector类,来保存map类中的数据。
★★★注:在map类中,iterator的返回类型是:pair<Key,T>。因此在创建 vector类时,默认参数为:pair<string,int>
问:这里为什么不用sort?而是用stale_sort,二者有什么区别?
答:编译不通过是一回事,更重要的是,排序的稳定性!
复习:排序分为稳定排序和不稳定排序,两者的区别在于:原本的数据如果已经满足排序要求,当前排序算法会不会改变原先的排列顺序,改变->不稳定排序;不改变->稳定排序
稳定排序:冒泡排序、插入排序、归并排序
不稳定排序:希尔排序、快速排序、堆排序等
其次:stable_sort 默认排序不满足我们的要求,因此需要自己写一个仿函数来实现我们所需的排序,如下:
struct compare { bool operator()(const pair<string,int>& x1,const pair<string,int>& x2) { return x1.second > x2.second; } };
compare() 涉及匿名对象调用的问题,可以通过两种方式来让stable_sort来调用我们的函数,一种是实例化compare对象,通过实例化的对象来调用,例如
compare com; stable_sort(v.begin(),v.end(),com);
此时不需要加括号,还有一种就是图中代码所写的匿名对象来调用。
注1:大多数时候,我们是不用写第三个参数的,只不过有些时候因为默认参数不满足我们的要求,因此这种时候需要通过仿函数来满足自己的要求。
注2:operator(),这个在优先队列一节中也曾使用过。
vector<string> str; for(int i = 0; i<k; i++) { str.push_back(v[i].first); }
此时v中的数据已经按题目要求进行排序,只需将前k个字母插入到 str 当中即可得到最终答案。
3:单词识别
3.1题目要求
3.2解题思路
个人感想:对于博主而言,一道里程碑级的题目
思路:
①:需要输入一句英文句子
string sentence; getline(cin,sentence);
②:因为string遍历句子识别的是一个个字母,因此接下来做的是识别单词,并将单词存入到vector<string>当中
string tmp; //临时存放识别完成的单词 vector<string> v;//将识别成功的单词存入到v中 for(int i = 0; i < sentence.size(); i++) { //.... //.... }
在遍历sentence[i]的过程中会遇到三中情况:
Ⅰ:为小写字母,此时直接 tmp.push_back(sentence[i]);
Ⅱ:为大写字母,转为小写后再 tmp.push_back(sentence[i]);
Ⅲ:其他,则说明此时一个单词已经识别完毕,即tmp中存储的是一个完整的单词,那么直接v.push_back(tmp); tmp.clear; 不要忘了清空tmp,为下一次识别做准备。
重复上述 Ⅰ、Ⅱ、Ⅲ 直至循环结束。
③:从这一步开始就和 第2题 的思路一模一样,创建 map<string,int> CountMap 记录每个单词出现的次数,将数据暂存进新的vector,并自己写一个满足题目要求的仿函数进行排序,最后再遍历输出目标结果
3.3实现代码
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
struct Com
{
bool operator()(const pair<string,int>& x1,const pair<string,int>& x2)
{
return x1.second > x2.second;
}
};
int main() {
//输入一个英文句子
string sentence;
getline(cin,sentence);
//将字母一一存到vector当中
vector<string> v;
string tmp;
for(int i = 0; i < sentence.size(); i++)
{
if(sentence[i] >='a' && sentence[i] <= 'z')
{
tmp.push_back(sentence[i]);
}else if (sentence[i] >='A' && sentence[i] <= 'Z') //大小写转换
{
sentence[i]+=32;
tmp.push_back(sentence[i]);
}
else
{
v.push_back(tmp);
tmp.clear();//清空tmp,为了下一次能够识别单词
}
}
//统计字母出现的次数
map<string,int> CountMap;
for(auto& e : v)
{
CountMap[e]++;
}
//将map中的数据默认是按key排列的,因此需要重新排列,保存到vector中进行重新排列
vector<pair<string,int>> v2(CountMap.begin(),CountMap.end());
stable_sort(v2.begin(),v2.end(),Com());//默认的函数不满足我们的排序,因此需要自己写一个仿函数,并且通过匿名对象调用
//最后遍历排列好的vector 输出最终答案
for(int i = 0; i < v2.size(); i++)
{
cout << v2[i].first << ":" << v2[i].second << endl;
}
return 0;
}