总览
- [LCR 021. 删除链表的倒数第 N 个结点](https://leetcode.cn/problems/SLwz0R/)
- [141. 环形链表](https://leetcode.cn/problems/linked-list-cycle/)
- [142. 环形链表 II](https://leetcode.cn/problems/linked-list-cycle-ii/)
- [LCR 032. 有效的字母异位词](https://leetcode.cn/problems/dKk3P7/)
- [349. 两个数组的交集](https://leetcode.cn/problems/intersection-of-two-arrays/)
- [1. 两数之和](https://leetcode.cn/problems/two-sum/)
- [454. 四数相加 II](https://leetcode.cn/problems/4sum-ii/)
- [15. 三数之和](https://leetcode.cn/problems/3sum/)
- [18. 四数之和](https://leetcode.cn/problems/4sum/)
- C++ 数组、set、map 对比
- C++ set / unordered_set / multiset 对比
- C++ map / unordered_map / multimap 对比
后面有 C++ 中哈希标中set,map,数组之间的区别。
LCR 021. 删除链表的倒数第 N 个结点
给定一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead -> next = head;
ListNode* left = dummyHead;
ListNode* right = dummyHead;
n++;
while(n--)
{
right = right -> next;
}
while(right)
{
right = right -> next;
left = left -> next;
}
ListNode* temp = left -> next;
left -> next = left -> next -> next;
delete(temp);
return dummyHead -> next;
}
};
141. 环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* slow = head;
ListNode* fast = head;
while(fast && fast -> next)
{
fast = fast -> next -> next;
slow = slow -> next;
if(fast == slow)
{
return true;
}
}
return false;
}
};
142. 环形链表 II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast && fast -> next)
{
slow = slow -> next;
fast = fast -> next -> next;
if(slow == fast)
{
while(head != fast)
{
head = head -> next;
fast = fast -> next;
}
return fast;
}
}
return nullptr;
}
};
LCR 032. 有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断它们是不是一组变位词(字母异位词)。
注意:若 *s* 和 *t* 中每个字符出现的次数都相同且字符顺序不完全相同,则称 *s* 和 *t* 互为变位词(字母异位词)。
class Solution {
public:
bool isAnagram(string s, string t) {
if(s.length() != t.length())
{
return false;
}
if(s == t)
{
return false;
}
vector<int> table (26,0);
for(auto& ch : s)
{
table[ch - 'a']++;
}
for(auto& ch : t)
{
table[ch - 'a']--;
if(table[ch - 'a'] < 0)
{
return false;
}
}
return true;
}
};
349. 两个数组的交集
给定两个数组 nums1 和 nums2 ,返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> st(nums1.begin(), nums1.end());
vector<int> ans;
for(int& x:nums2)
{
if(st.erase(x))
ans.push_back(x);
}
return ans;
}
};
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> idx;
for(int j = 0; ;j++)
{
auto it = idx.find(target - nums[j]);
if(it != idx.end())
{
return {it -> second , j};
}
idx[nums[j]] = j;
}
}
};
454. 四数相加 II
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < nnums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int> cnt;
for(int&a :nums1)
{
for(int&b : nums2)
{
cnt[a+b]++;
}
}
int ans = 0;
for(int&c :nums3)
{
for(int&d : nums4)
{
int target = 0 - c - d;
ans += cnt[target];
}
}
return ans;
}
};
15. 三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
**注意:**答案中不可以包含重复的三元组。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> ans;
int n = nums.size();
for(int i = 0;i < n - 2;i++)
{
int x = nums[i];
if(i && x == nums[i - 1]) continue;
if(x + nums[i+1] + nums[i+2] > 0) break;
if(x + nums[n-1] + nums[n-2] < 0) continue;
int left = i + 1;
int right = n - 1;
while(left < right)
{
int s = x + nums[left] + nums[right];
if(s > 0)
{
right--;
}
else if(s < 0)
{
left++;
}
else
{
ans.push_back({x,nums[left],nums[right]});
left++;
right--;
while(left < right && nums[left] == nums[left-1]) left++;
while(left < right && nums[right] == nums[right+1]) right--;
}
}
}
return ans;
}
};
18. 四数之和
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < na、b、c和d互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> ans;
ranges::sort(nums);
int n = nums.size();
for(int k = 0;k < n - 3;k++)
{
long long x = nums[k];
if(x + nums[k+1] + nums[k+2] + nums[k+3] > target) break;
if(x + nums[n-1] + nums[n-2] + nums[n-3] < target) continue;
if(k > 0 && nums[k] == nums[k-1]) continue;
for(int i = k+1;i < n - 2;i++)
{
long long y = nums[i];
if(x + y + nums[i+1] + nums[i+2] > target) break;
if(x + nums[n-1] + nums[n-2] + y < target) continue;
if(i > k + 1 && y == nums[i - 1]) continue;
int left = i + 1;
int right = n - 1;
while(left < right)
{
long long s = x + y + nums[left] + nums[right] - target;
if(s > 0) right--;
else if(s < 0) left++;
else
{
ans.push_back({(int)x,(int)y,nums[left],nums[right]});
for(left++;left < right && nums[left] == nums[left - 1];left++);
for(right--;left < right && nums[right] == nums[right + 1];right--);
}
}
}
}
return ans;
}
};
C++ 数组、set、map 对比
| 特性 | 数组 (array/vector) | set | map |
|---|---|---|---|
| 存储内容 | 单一值 | 单一值(不重复) | 键值对(key-value) |
| 元素顺序 | 按插入顺序/索引 | 自动排序 | 按 key 自动排序 |
| 重复元素 | ✅ 允许 | ❌ 不允许 | key 不允许,value 允许 |
| 访问方式 | 索引 arr[i] | 迭代器/find | map[key] 或 find |
| 查找复杂度 | O(n) | O(log n) | O(log n) |
| 插入复杂度 | O(1) 尾部 / O(n) 中间 | O(log n) | O(log n) |
| 底层实现 | 连续内存 | 红黑树 | 红黑树 |
1. 数组 / vector
#include <vector>
#include <iostream>
int main() {
std::vector<int> arr = {3, 1, 4, 1, 5}; // 允许重复
arr.push_back(9); // 尾部添加
arr[0] = 10; // 按索引修改
for (int x : arr) {
std::cout << x << " "; // 输出: 10 1 4 1 5 9
}
return 0;
}
适用场景:需要按位置访问、允许重复、频繁尾部操作
2. set
#include <set>
#include <iostream>
int main() {
std::set<int> s = {3, 1, 4, 1, 5}; // 重复的 1 会被忽略
s.insert(2);
s.erase(4);
for (int x : s) {
std::cout << x << " "; // 输出: 1 2 3 5(自动排序)
}
// 查找
if (s.find(3) != s.end()) {
std::cout << "\n找到 3" << std::endl;
}
// C++20: s.contains(3)
return 0;
}
适用场景:去重、快速判断元素是否存在、需要有序
3. map
#include <map>
#include <iostream>
#include <string>
int main() {
std::map<std::string, int> m;
m["apple"] = 5;
m["banana"] = 3;
m["apple"] = 10; // 覆盖原值
// 遍历(按 key 字母序排序)
for (auto& [key, value] : m) {
std::cout << key << ": " << value << std::endl;
}
// 输出:
// apple: 10
// banana: 3
// 查找
if (m.find("apple") != m.end()) {
std::cout << "apple 的值: " << m["apple"] << std::endl;
}
return 0;
}
适用场景:键值对映射、根据 key 快速查 value、统计计数
无序版本(哈希表,O(1) 查找)
| 有序版 | 无序版(更快) |
|---|---|
std::set | std::unordered_set |
std::map | std::unordered_map |
#include <unordered_map>
std::unordered_map<std::string, int> um;
um["key"] = 100; // O(1) 平均查找/插入
快速选择指南
需要按索引访问? → vector
需要去重? → set / unordered_set
需要键值对映射? → map / unordered_map
需要排序? → set / map
只需要快速查找(不排序)? → unordered_set / unordered_map
允许重复且需要排序? → multiset / multimap
C++ set / unordered_set / multiset 对比
| 特性 | set | unordered_set | multiset |
|---|---|---|---|
| 重复元素 | ❌ 不允许 | ❌ 不允许 | ✅ 允许 |
| 元素顺序 | 有序(升序) | 无序 | 有序(升序) |
| 底层实现 | 红黑树 | 哈希表 | 红黑树 |
| 查找/插入 | O(log n) | O(1) 平均 | O(log n) |
| 头文件 | <set> | <unordered_set> | <set> |
1. set(有序 + 去重)
#include <set>
#include <iostream>
int main() {
std::set<int> s = {5, 2, 8, 2, 1, 5};
s.insert(3);
for (int x : s) {
std::cout << x << " ";
}
// 输出: 1 2 3 5 8(自动排序,重复被去除)
// 查找
if (s.count(5)) { // count 只返回 0 或 1
std::cout << "\n找到 5";
}
return 0;
}
2. unordered_set(无序 + 去重 + 更快)
#include <unordered_set>
#include <iostream>
int main() {
std::unordered_set<int> us = {5, 2, 8, 2, 1, 5};
us.insert(3);
for (int x : us) {
std::cout << x << " ";
}
// 输出顺序不确定,如: 1 8 2 5 3(去重但无序)
// 查找 O(1)
if (us.find(5) != us.end()) {
std::cout << "\n找到 5";
}
return 0;
}
3. multiset(有序 + 允许重复)
#include <set>
#include <iostream>
int main() {
std::multiset<int> ms = {5, 2, 8, 2, 1, 5};
ms.insert(2); // 可以继续插入重复值
for (int x : ms) {
std::cout << x << " ";
}
// 输出: 1 2 2 2 5 5 8(有序,保留所有重复)
// count 返回该元素的个数
std::cout << "\n2 出现了 " << ms.count(2) << " 次"; // 输出: 3
// 删除所有值为 2 的元素
ms.erase(2); // 删除全部
// 只删除一个
// ms.erase(ms.find(2));
return 0;
}
删除操作区别(multiset 注意点)
std::multiset<int> ms = {1, 2, 2, 2, 3};
// 方式1: erase(值) —— 删除所有该值
ms.erase(2); // 删除全部 2,剩 {1, 3}
// 方式2: erase(迭代器) —— 只删除一个
ms.erase(ms.find(2)); // 只删除一个 2,剩 {1, 2, 2, 3}
实际应用场景
| 容器 | 典型场景 |
|---|---|
| set | 去重 + 需要有序输出、维护有序集合 |
| unordered_set | 快速判断元素存在性、大数据去重 |
| multiset | 维护有序序列允许重复、滑动窗口中位数、优先队列替代 |
快速选择
需要去重吗?
├── 是 → 需要有序吗?
│ ├── 是 → set
│ └── 否 → unordered_set(更快)
└── 否 → 需要有序吗?
├── 是 → multiset
└── 否 → vector(直接存)
补充:unordered_multiset
还有 unordered_multiset:无序 + 允许重复
#include <unordered_set>
std::unordered_multiset<int> ums = {1, 2, 2, 3};
ums.insert(2);
std::cout << ums.count(2); // 输出: 3
C++ map / unordered_map / multimap 对比
| 特性 | map | unordered_map | multimap |
|---|---|---|---|
| key 重复 | ❌ 不允许 | ❌ 不允许 | ✅ 允许 |
| 元素顺序 | 按 key 有序 | 无序 | 按 key 有序 |
| 底层实现 | 红黑树 | 哈希表 | 红黑树 |
| 查找/插入 | O(log n) | O(1) 平均 | O(log n) |
[] 访问 | ✅ 支持 | ✅ 支持 | ❌ 不支持 |
| 头文件 | <map> | <unordered_map> | <map> |
1. map(key 有序 + 不重复)
#include <map>
#include <iostream>
#include <string>
int main() {
std::map<std::string, int> m;
m["banana"] = 3;
m["apple"] = 5;
m["cherry"] = 2;
m["apple"] = 10; // 覆盖原值
// 按 key 字母序遍历
for (auto& [key, val] : m) {
std::cout << key << ": " << val << std::endl;
}
// 输出:
// apple: 10
// banana: 3
// cherry: 2
// 查找
if (m.find("apple") != m.end()) {
std::cout << "apple = " << m["apple"] << std::endl;
}
// [] 访问不存在的 key 会自动创建(值初始化为 0)
std::cout << m["durian"] << std::endl; // 输出: 0
return 0;
}
2. unordered_map(key 无序 + 不重复 + 更快)
#include <unordered_map>
#include <iostream>
#include <string>
int main() {
std::unordered_map<std::string, int> um;
um["banana"] = 3;
um["apple"] = 5;
um["cherry"] = 2;
// 遍历顺序不确定
for (auto& [key, val] : um) {
std::cout << key << ": " << val << std::endl;
}
// 输出顺序可能是: cherry: 2, apple: 5, banana: 3
// O(1) 查找
if (um.count("apple")) {
std::cout << "apple = " << um["apple"] << std::endl;
}
// 统计字符出现次数(经典用法)
std::string s = "hello";
std::unordered_map<char, int> freq;
for (char c : s) {
freq[c]++;
}
// freq: h=1, e=1, l=2, o=1
return 0;
}
3. multimap(key 有序 + 允许重复 key)
#include <map>
#include <iostream>
#include <string>
int main() {
std::multimap<std::string, int> mm;
mm.insert({"apple", 5});
mm.insert({"banana", 3});
mm.insert({"apple", 10}); // 同一 key 可以有多个值
mm.insert({"apple", 8});
// 按 key 有序遍历
for (auto& [key, val] : mm) {
std::cout << key << ": " << val << std::endl;
}
// 输出:
// apple: 5
// apple: 10
// apple: 8
// banana: 3
// ❌ 不能用 mm["apple"],只能用 find
// 查找某个 key 的所有值
auto range = mm.equal_range("apple");
std::cout << "\napple 的所有值: ";
for (auto it = range.first; it != range.second; ++it) {
std::cout << it->second << " ";
}
// 输出: 5 10 8
// count 返回该 key 的元素个数
std::cout << "\napple 有 " << mm.count("apple") << " 个" << std::endl;
return 0;
}
重要操作对比
插入
// map / unordered_map
m["key"] = value; // 覆盖式插入
m.insert({"key", value}); // 如果 key 存在则不插入
// multimap(不支持 [])
mm.insert({"key", value}); // 总是插入,即使 key 存在
删除
// map / unordered_map
m.erase("key"); // 删除该 key
// multimap
mm.erase("apple"); // 删除所有 key="apple" 的元素
mm.erase(mm.find("apple")); // 只删除一个
查找 multimap 的所有值
std::multimap<int, std::string> mm;
mm.insert({1, "a"});
mm.insert({1, "b"});
mm.insert({1, "c"});
// 方法1: equal_range
auto [begin, end] = mm.equal_range(1);
for (auto it = begin; it != end; ++it) {
std::cout << it->second << " ";
}
// 方法2: 循环(C++17)
for (auto it = mm.find(1); it != mm.end() && it->first == 1; ++it) {
std::cout << it->second << " ";
}
实际应用场景
| 容器 | 典型场景 |
|---|---|
| map | 需要按 key 排序、有序遍历字典 |
| unordered_map | 快速查找、统计频率、缓存映射 |
| multimap | 一个 key 对应多个 value、存储一对多关系 |
例子:
// 统计每个单词出现次数 → unordered_map
std::unordered_map<std::string, int> wordCount;
// 按成绩分组学生(成绩可能相同)→ multimap
std::multimap<int, std::string> scoreToName;
scoreToName.insert({90, "Alice"});
scoreToName.insert({90, "Bob"});
scoreToName.insert({85, "Charlie"});
快速选择
key 会重复吗?
├── 否 → 需要按 key 排序吗?
│ ├── 是 → map
│ └── 否 → unordered_map(更快)
└── 是 → 需要按 key 排序吗?
├── 是 → multimap
└── 否 → unordered_multimap
补充:unordered_multimap
#include <unordered_map>
std::unordered_multimap<std::string, int> umm;
umm.insert({"apple", 5});
umm.insert({"apple", 10});
std::cout << umm.count("apple"); // 输出: 2
无序 + 允许 key 重复,但实际用得较少。
522



被折叠的 条评论
为什么被折叠?



