题目一:454. 四数相加 II
解法:分组哈希法
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> map;
int ans = 0;
// 计算 nums1 + nums2 的所有和
for (int x : nums1) {
for (int y : nums2) {
map[x + y]++;
}
}
// 查找 nums3 + nums4 的补数
for (int x : nums3) {
for (int y : nums4) {
ans += map[-(x + y)];
}
}
return ans;
}
};
核心思路:将4个数组分成两组,降低时间复杂度从O(n⁴)到O(n²)
题目二:383. 赎金信
解法1:哈希表法(你的错误版本)
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
unordered_map<char, int> map;
for (auto elem : ransomNote) { // 错误:先统计需求
map[elem]++;
}
for (auto elem : magazine) { // 再减去供给
map[elem]--;
if (map[elem] < 0) return false; // 逻辑错误
}
return true;
}
};
解法2:哈希表法(正确版本)
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
unordered_map<char, int> map;
// 先统计供给(magazine)
for (auto elem : magazine) {
map[elem]++;
}
// 再检查需求(ransomNote)
for (auto elem : ransomNote) {
map[elem]--;
if (map[elem] < 0) return false; // 供给不足
}
return true;
}
};
解法3:数组计数法(最优)
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
if (ransomNote.size() > magazine.size()) return false;
int count[26] = {0};
// 统计供给
for (char c : magazine) {
count[c - 'a']++;
}
// 检查需求
for (char c : ransomNote) {
if (count[c - 'a'] <= 0) return false;
count[c - 'a']--;
}
return true;
}
};
题目三:15. 三数之和
解法:排序 + 双指针
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
ranges::sort(nums); // 关键:先排序
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 - 2] + nums[n - 1] < 0) continue;
int j = i + 1, k = n - 1; // 双指针
while (j < k) {
int s = x + nums[j] + nums[k];
if (s > 0) k--;
else if (s < 0) j++;
else {
ans.push_back({x, nums[j], nums[k]});
// 去重
for (k--; k > j && nums[k] == nums[k + 1]; k--);
for (j++; j < k && nums[j] == nums[j - 1]; j++);
}
}
}
return ans;
}
};
题目四:18. 四数之和
解法:排序 + 双指针(三数之和的扩展)
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
ranges::sort(nums);
vector<vector<int>> ans;
int n = nums.size();
for (int a = 0; a < n - 3; a++) {
long long x = nums[a];
// 去重
if (a && x == nums[a - 1]) continue;
// 剪枝优化
if (x + nums[a + 1] + nums[a + 2] + nums[a + 3] > target) break;
if (x + nums[n - 3] + nums[n - 2] + nums[n - 1] < target) continue;
for (int b = a + 1; b < n - 2; b++) {
long long y = nums[b];
// 去重
if (b > a + 1 && y == nums[b - 1]) continue;
// 剪枝优化
if (x + y + nums[b + 1] + nums[b + 2] > target) break;
if (x + y + nums[n - 2] + nums[n - 1] < target) continue;
int c = b + 1, d = n - 1; // 双指针
while (c < d) {
long long s = x + y + nums[c] + nums[d];
if (s > target) d--;
else if (s < target) c++;
else {
ans.push_back({(int)x, (int)y, nums[c], nums[d]});
// 去重
for (c++; c < d && nums[c] == nums[c - 1]; c++);
for (d--; d > c && nums[d] == nums[d + 1]; d--);
}
}
}
}
return ans;
}
};
核心技巧总结
| 技巧 | 适用场景 | 代表题目 | 时间复杂度 |
|---|---|---|---|
| 分组哈希 | 多个数组组合问题 | 四数相加II | O(n²) |
| 数组计数 | 字符统计、小范围数值 | 赎金信 | O(n) |
| 排序+双指针 | 多数和问题、需要去重 | 三数之和、四数之和 | O(n²)~O(n³) |
| 剪枝优化 | 有序数组的搜索问题 | 三/四数之和 | 大幅提升效率 |
关键区别
四数相加II vs 三/四数之和:
- 四数相加II:四个独立数组,只求个数 → 分组哈希
- 三/四数之和:单个数组,需要具体解且去重 → 排序+双指针
赎金信的关键:
- 统计顺序:先统计供给(magazine),再检查需求(ransomNote)
- 数据结构:优先使用数组而非哈希表(字符有限)
解题模板
多数和问题模板:
1. 排序数组
2. 外层循环固定第一个数(去重)
3. 剪枝优化
4. 内层使用双指针查找
5. 找到解后双指针去重
分组哈希模板:
1. 将问题分成两组
2. 计算第一组的所有可能(存入map)
3. 在第二组中查找补数