1. 复制带随机指针的链表
描述: 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。
新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点。
思路: 用
map
解决复杂链表的复制
🐎C语言写法:
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
struct Node* copyRandomList(struct Node* head) {
if (head == NULL)
return NULL;
struct Node* cur = head;
struct Node* copyhead = head;
// 1.
// 遍历源节点,并将新开辟的复制结点,放在源节点之后
while (cur)
{
struct Node* copy = (struct Node*)malloc(sizeof(struct Node)); // 新建结点
copy->val = cur->val; // 值
copy->next = cur->next; // 链接
cur->next = copy; //链接
// copy链表的头结点设置
if (copyhead == head)
{
copyhead = copy;
}
cur = copy->next;
}
// 2.
// 设置复制节点的random,如果该复制节点的源节点的random不为空,指向random,否则赋空
cur = head;
while (cur)
{
struct Node* copynode = cur->next;
if (cur->random)
{
copynode->random = cur->random->next;
}
else
{
copynode->random = NULL;
}
cur = copynode->next;
}
// 3.
// 把拷贝结点解下来,链接组成拷贝链表
cur = head;
while (cur)
{
struct Node* copynode = cur->next;
cur->next = copynode->next;
cur = cur->next;
if (cur)
{
copynode->next = cur->next;
copynode = copynode->next;
}
else
{
copynode->next = NULL;
}
}
return copyhead;
}
-
🐎C++写法
-
1.先尾插复制出一个单链表
-
2.用 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*> copyNodeMap;
Node* cur = head;
Node* copyhead = nullptr;
Node* copytail = nullptr;
// 1. 链接
while (cur)
{
Node* copynode = new Node(cur->val);
copyNodeMap[cur] = copynode; // 插入+修改 copyNodeMap[源节点] = 复制节点(此时只有val 指针为null的)
if (copytail == nullptr)
{
copytail = copyhead = copynode;
}
else
{
copytail->next = copynode;
copytail = copytail->next;
}
cur = cur->next;
}
// 位置重置
cur = head;
Node* copynode = copyhead;
// 2. 处理random
while (cur)
{
if (cur->random == nullptr)
{
copynode->random = nullptr;
}
else
{
copynode->random = copyNodeMap[cur->random]; // 寻找
}
cur = cur->next;
copynode = copynode->next;
}
return copyhead;
}
};
2. 前K个高频单词
描述: 给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。
返回的答案应该按单词出现频率由高到低排序。
如果不同的单词有相同出现频率, 按 字典顺序 排序。
要求分析:
a. 统计次数 ----> map 统计频次,按 key 排(字典顺序)
b. 频次前k ----> 排序的 稳定性 考查
c. 相同频次按 字典顺序
思路: 排序稳定性、仿函数控制比较逻辑
🐎:方法一:stable_sort
// 方法一:stable_sort
class Solution {
public:
struct Compare
{
bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2)
{
return kv1.second > kv2.second;
}
};
vector<string> topKFrequent(vector<string>& words, int k) {
// 1. 统计次数
map<string, int> countMap; // map排序是按key排的,不是频次value,此时已经是字典顺序
for (auto& str : words)
{
countMap[str]++;
}
// 2. 按次序排序:同时,次序相同,要求字典顺序排序
// 考查:排序的稳定性
// sort 是快排,要求是随机迭代器。故不能作用到countmap的迭代器(双向迭代器)上。另:快排是不稳定的
// 还是写一下sort的过程:将pair插入vecter再进行sort
vector<pair<string, int>> v(countMap.begin(), countMap.end());
//sort(v.begin(),v.end(), Compare()); // 这里是按first比,我们需要的是按second比,加一个仿函数
// 实际上:C++提供了一个稳定的排序--stable_sort
stable_sort(v.begin(), v.end(), Compare());
vector<string> ret;
for (size_t i = 0; i < k; ++i)
{
ret.push_back(v[i].first);
}
return ret;
}
};
🐎: 方法二:sort
,仿函数控制比较逻辑
// 方法二:sort,仿函数控制比较逻辑
class Solution {
public:
struct Compare
{
bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2)
{
return kv1.second > kv2.second || (kv1.second == kv2.second && kv1.first < kv2.first);
}
};
vector<string> topKFrequent(vector<string>& words, int k) {
// 1. 统计次数
map<string, int> countMap; // map排序是按key排的,不是频次value,此时已经是字典顺序
for (auto& str : words)
{
countMap[str]++;
}
// 2. 按次序排序:同时,次序相同,要求字典顺序排序
// 考查:排序的稳定性
// sort 是快排,要求是随机迭代器。故不能作用到countmap的迭代器(双向迭代器)上。另:快排是不稳定的
// sort的过程:将pair插入vecter再进行sort,并通过仿函数来控制“稳定性”!!!!
// 注意:表面上看起来似乎是控制了稳定性,但实际上是控制的比较逻辑
vector<pair<string, int>> v(countMap.begin(), countMap.end());
sort(v.begin(), v.end(), Compare()); // 这里是按first比,我们需要的是按second比,加一个仿函数(这里是函数参数,传的是对象,这里加括号是传的匿名对象
// 实际上:C++提供了一个稳定的排序--stable_sort
//stable_sort(v.begin(),v.end(), Compare());
vector<string> ret;
for (size_t i = 0; i < k; ++i)
{
ret.push_back(v[i].first);
}
return ret;
}
};
🐎 方法三:multimap 排序,模板参数中添加比较方法
// 方法三:multimap排序,模板参数中添加比较方法
// but!!!这题纯属运气好,multimap相等恰好是插入在右边的!!!如果是相等插入在左边就不通过喽
class Solution {
public:
vector<string> topKFrequent(vector<string>& words, int k) {
// 1. 统计次数
map<string, int> countMap; // map排序是按key排的,不是频次value,此时已经是字典顺序
for (auto& str : words)
{
countMap[str]++;
}
// 2. 按次序排序:同时,次序相同,要求字典顺序排序
// 考查:排序的稳定性
// map 是排序+去重,我们要选用 multimap,同样可以传Compare(),我们对其进行指定比较逻辑
multimap<int, string, greater<int>> sortMap; // (类模板参数,传的是类型!!!
for (auto& kv : countMap)
{
sortMap.insert(make_pair(kv.second, kv.first));
}
// 3. 取出前k个
vector<string> ret;
auto it = sortMap.begin();
while (k--)
{
ret.push_back(it->second);
it++;
}
return ret;
}
};
🐎 方法四:multiset 排序,仿函数控制比较逻辑
// 方法四:multiset 排序,仿函数控制比较逻辑
// 实际上,指定比较逻辑后,可以使用 set 了,此时已经不是光比较 key 了
class Solution {
public:
struct Compare
{
bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2) const // 有可能用const对象进行调用,所以加const
{
return kv1.second > kv2.second || (kv1.second == kv2.second && kv1.first < kv2.first);
}
};
vector<string> topKFrequent(vector<string>& words, int k) {
// 1. 统计次数
map<string, int> countMap; // map排序是按key排的,不是频次value,此时已经是字典顺序
for (auto& str : words)
{
countMap[str]++;
}
// 2. 按次序排序:同时,次序相同,要求字典顺序排序
// 考查:排序的稳定性
// multiset
multiset<pair<string, int>, Compare> sortSet(countMap.begin(), countMap.end());
// 3. 取出前k个
vector<string> ret;
auto it = sortSet.begin();
while (k--)
{
ret.push_back(it->first);
it++;
}
return ret;
}
};
3. 两个数组的交集
描述: 给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
分析思路:
set
排序 + 去重- 找交集 a.不相等,小的++ b.相等,是交集同时++
- 一个集合走完就结束
找差集思路:
- 排序 + 去重
- 找差集 a.相等同时++ b.不相等,小的就是差集,小的++
- 一个走完了,剩下没有走完的也是差集
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
// 1. 排序 + 去重
// sort(nums1.begin(), nums1.end());
// unique(nums1.begin(), nums1.end());
set<int> s1(nums1.begin(), nums1.end());
set<int> s2(nums2.begin(), nums2.end());
// 2. 找交集
auto it1 = s1.begin();
auto it2 = s2.begin();
vector<int> ret;
while (it1 != s1.end() && it2 != s2.end())
{
// a. 如果相等,是交集
if (*it1 == *it2)
{
ret.push_back(*it1);
++it1;
++it2;
}
// b. 如果不等,小的那个++
else
{
*it1 < *it2 ? ++it1 : ++it2;
}
}
return ret;
}
};