目录
(六)四数相加Ⅱ
1. 题目描述
给你四个整数数组 nums1
、nums2
、nums3
和 nums4
,数组长度都是 n
,请你计算有多少个元组 (i, j, k, l)
能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
2. 思路
- 首先定义 一个 unordered_map,key 放 a 和 b 两数之和,value 放 a 和 b 两数之和出现的次数。
- 遍历大 A 和大 B 数组,统计两个数组元素之和,和出现的次数,放到 map 中。
- 定义 int 变量 count,用来统计 a+b+c+d = 0 出现的次数。
- 在遍历大 C 和大 D 数组,找到如果 0-(c+d) 在 map 中出现过的话,就用 count 把 map 中 key 对应的 value 也就是出现次数统计出来。
- 最后返回统计值 count。
3. 解题过程
难易程度:中等
标签:数组、哈希表
result 计算的时候是加上有几个等于此值的和,而不是++,第一次犯了这个错误。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> map; // nums1和nums2的两元素和与出现次数
for(int i = 0; i < nums1.size(); i++){
for(int j = 0; j < nums2.size(); j++){
auto it = map.find(nums1[i] + nums2[j]);
if(it != map.end()){
it->second++;
}
map.insert(pair<int, int>(nums1[i] + nums2[j], 1));
}
}
int result = 0;
for(int i = 0; i < nums3.size(); i++){
for(int j = 0; j < nums4.size(); j++){
auto it = map.find(-nums3[i] - nums4[j]);
if(it != map.end()){
result += it->second;
}
}
}
return result;
}
};
注意:其实不需要迭代器插入和让 value 自增,直接 map[ nums1[i] + nums2[j] ]++就可以了。
(七)赎金信
上一篇的相关题目有这个,做过了。
(八)三数之和
1. 题目描述
给你一个整数数组 nums
,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j
、i != k
且 j != k
,同时还满足 nums[i] + nums[j] + nums[k] == 0
。请
你返回所有和为 0
且不重复的三元组。
注意:答案中不可以包含重复的三元组。
2. 思路
可以使用哈希法来确定 0-(a+b) 是否在 数组里出现过,但题目中说的不可以包含重复的三元组,把符合条件的三元组放进vector中,然后再去重,这样是非常费时的,很容易超时,所以使用双指针。
3. 解题过程
难易程度:中等
标签:数组、双指针、排序
(1)超时的代码
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
unordered_map<int, int> num_map; // 把数字和下标存在map
vector<vector<int>> result;
for(int i = 0; i < nums.size(); i++){
num_map[nums[i]] = i;
}
for(int i = 0; i < nums.size(); i++){
for(int j = i + 1; j < nums.size(); j++){
auto it = num_map.find(-nums[i] - nums[j]);
if(it != num_map.end()){
if(it->second != i && it->second != j){
vector<int> temp(3);
temp = {nums[i], nums[j], it->first};
sort(temp.begin(), temp.end());
result.push_back(temp);
continue;
}
}
}
}
sort(result.begin(), result.end());
result.erase(unique(result.begin(), result.end()), result.end());
return result;
}
};
(2)代码随想录的正确哈希
这些去重实在有点过于复杂。。。还是放弃这种方法(
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
// 找出a + b + c = 0
// a = nums[i], b = nums[j], c = -(a + b)
for (int i = 0; i < nums.size(); i++) {
// 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
if (nums[i] > 0) {
break;
}
if (i > 0 && nums[i] == nums[i - 1]) { //三元组元素a去重
continue;
}
unordered_set<int> set;
for (int j = i + 1; j < nums.size(); j++) {
if (j > i + 2
&& nums[j] == nums[j-1]
&& nums[j-1] == nums[j-2]) { // 三元组元素b去重
continue;
}
int c = 0 - (nums[i] + nums[j]);
if (set.find(c) != set.end()) {
result.push_back({nums[i], nums[j], c});
set.erase(c);// 三元组元素c去重
} else {
set.insert(nums[j]);
}
}
}
return result;
}
};
(3)双指针
首先将数组排序,然后有一层 for 循环,i 从下标 0 的地方开始,同时定一个下标 left 定义在 i+1 的位置上,right 在数组结尾的位置上。
如果 nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,right 向左移动;如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,直到 left 与 right 相遇为止。
注意:去重要在过程中完成,排序之后去掉连续的重复的。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for(int i = 0; i < nums.size(); i++){
if(nums[i] > 0){
return result;
}
// 第一个数去重
if(i > 0 && nums[i] == nums[i - 1]){
continue;
}
for(int left = i + 1, right = nums.size() - 1; left < right;){
int temp = nums[i] + nums[left] + nums[right];
if(temp == 0){
result.push_back({nums[i], nums[left], nums[right]});
// 第二个数去重
while(left < right && nums[left] == nums[left + 1]){
left++;
}
// 第三个数去重
while(left < right && nums[right] == nums[right - 1]){
right--;
}
left++;
right--;
}
else if(temp < 0){
left++;
}
else{
right--;
}
}
}
return result;
}
};
(九)四数之和
1. 题目描述
给你一个由 n
个整数组成的数组 nums
,和一个目标值 target
。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]]
(若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a
、b
、c
和d
互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
2. 思路
四数之和,和三数之和是一个思路,都是使用双指针法, 基本解法就是在三数之和的基础上再套一层for循环。
但是有一些细节需要注意,例如: 不要判断 nums[k] > target
就返回了,三数之和 可以通过 nums[i] > 0
就返回,因为 0 已经是确定的数了,四数之和这道题目 target 是任意值。比如:数组是 [-4, -3, -2, -1]
,target
是 -10
,不能因为 -4 > -10
而跳过。但是我们依旧可以去做剪枝,逻辑变成 nums[i] > target && (nums[i] >=0 || target >= 0)
就可以了。
3. 解题过程
难易程度:中等
标签:数组、双指针、排序
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
vector<vector<int>> result;
for(int i = 0; i < nums.size(); i++){
// 这里可以直接return,因为如果第一个数就大于那么一定不可能加后面的得到结果
if(nums[i] > target && nums[i] >= 0){
return result;
}
if(i > 0 && nums[i] == nums[i - 1]){
continue;
}
for(int j = i + 1; j < nums.size(); j++){
// 要用break而不可以直接return,因为i+1还有可能有符合条件的
if(nums[i] + nums[j] > target && nums[i] + nums[j] >= 0){
break;
}
if(j > i + 1 && nums[j] == nums[j - 1]){
continue;
}
int left = j + 1;
int right = nums.size() - 1;
while(left < right){
// 注意范围,加的结果可能超出int界限
long long temp = (long long)nums[i] + nums[j] + nums[left] + nums[right];
if(temp == target){
result.push_back({nums[i], nums[j], nums[left], nums[right]});
while(left < right && nums[left] == nums[left + 1]){
left++;
}
while(left < right && nums[right] == nums[right - 1]){
right--;
}
left++;
right--;
}
else if(temp < target){
left++;
}
else{
right--;
}
}
}
}
return result;
}
};
(十)总结
一般来说哈希表都是用来快速判断一个元素是否出现集合里。
对于哈希表,要知道哈希函数和哈希碰撞在哈希表中的作用。
哈希函数是把传入的key映射到符号表的索引上。
哈希碰撞处理有多个key映射到相同索引上时的情景,处理碰撞的普遍方式是拉链法和线性探测法。
在C++中,set 和 map 都分别提供了三种数据结构,每种数据结构的底层实现和用途都有所不同,要根据他们的特点,判断使用哪种结构。