LeetCode 454.四数相加II
题目链接🔗:454.四数相加II
解题思路🤔
其实这道题完全可以看做是稍有变动的1. 两数之和,即把A[i] + B[j] + C[k] + D[l] = 0
看作(A[i] + B[j]) + (C[k] + D[l]) = 0
。因此,思路就很好想了。
还是那四个问题:
- 为什么会想到用哈希表
- 当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法
- 哈希表为什么用map
- 题目要求找到两个不同的目标,k-v键值对更易于保存
- 本题map是用来存什么的
- 存放遍历过的元素
- map中的key和value用来存什么的
- key:
A[i] + B[j]
的和,value:A[i] + B[j]
的和出现的次数
- key:
重点分析一下key与value的存放:
- 在对比时的判断条件是
(0 - (A[k] + B[l]))
在map中是否出现过(判断(0 - (A[i] + B[j]))
在map中是否出现当然也可以,这里是为了方便选择了前者),因此用于对比的key
就使用(A[i] + B[j])
; - 题目中要求返回符合条件的元组有多少个,我们定义一个变量
count
来统计,只要出现(A[i] + B[j]) + (C[k] + D[l]) = 0
,即(0 - (C[k] + D[l]))
在map中出现过,就让count
+ 1,最后返回count
即可。因此,value
的值就是A[i] + B[j]
的和出现的次数。
遇到的问题😢
无
代码实现👨🏻💻
第一种方法
var fourSumCount = function(nums1, nums2, nums3, nums4) {
const map = new Map(); // 定义Map结构
for (const i of nums1) { // 遍历nums1、nums2,这里i与j就代表着nums[i]、nums[j]
for (const j of nums2) {
// 将i + j的和放入key,出现此处放入value,get()获取map的value值,出现就将其加入,未出现则加入0。
// +1用来保证第一次获取value时添加的就是1,第二次循环get()返回的是1,再+1就变为2,以此类推
map.set(i + j, (map.get(i + j) || 0) + 1);
}
}
let count = 0;
for (const k of nums3) { // 遍历nums3、nums4,这里k与l就代表着nums[k]、nums[l]
for (const l of nums4) {
if (map.has(0 - (k + l))) { // 判断map中是否存在0 - (k + l) = i+ j
count += (map.get(0 - (k + l)) || 0); // 如果存在就让count + 1
}
}
}
return count;
};
总结📖
第一种方法
有了1. 两数之和的基础后这道题并不难想,主要注意在向map中添加(A[i] + B[j]
时要注意value
如何添加。
LeetCode 383. 赎金信
题目链接🔗:383. 赎金信
解题思路🤔
整体思路有点像242.有效的字母异位词,采用哈希法,用长度为26的数组来记录magazine
串中出现的字母,再用ransomNote
对比,判断数组中是否包含了能组成randomNote
串的字母。
遇到的问题😢
无
代码实现👨🏻💻
哈希表法
var canConstruct = function(ransomNote, magazine) {
const arr = new Array(26).fill(0); // 定义数组
const k = "a".charCodeAt(); // 记录a的ASCII码,用于计算与串中对应字符的ASCII码的差值
// 记录magazine串中字母出现的次数,出现则在对应位置+1
for (const i of magazine) arr[i.charCodeAt() - k]++;
// 与ransomNote对比
for (const j of ransomNote) {
const index = j.charCodeAt() - k; // 定义index存放ransomNote串中每个字符与k的ASCII码的差值
if (!arr[index]) return false; // 未匹配成功,返回false
arr[index]--; // 匹配成功,减去数组中对应的字符出现的次数
}
return true;
};
总结📖
哈希表法
注意对数据结构的选择,数组更节约资源。
LeetCode 15. 三数之和
题目链接🔗:15. 三数之和
解题思路🤔
三个互不相同的元素相加后的结果为0。总结一下卡哥的题解:
- 首先排序数组为非递减数组,然后定义指针
i
指向下标0,指针left
指向i + 1
,指针right
指向末尾元素 - 要完成的任务变为
nums[i] + nums[left] + nums[right] = 0
- for循环让
i
遍历数组,如果nums[i] + nums[left] + nums[right] > 0
说明,三数之和过大了,right
指针要向左移动,让三数之和变小一点; - 如果
nums[i] + nums[left] + nums[right] < 0
说明,三数之和过小了,left
指针要向右移动,让三数之和变大一点。 - 直到
left
与right
相遇,此时i
进行下一次移动,left
回到当前的i + 1
位置,重复寻找操作。 - 找到的元组存在重复的可能。因此还要进行去重,去重操作要完成的是不能有重复的三元组,但三元组内的元素是可以重复的,要判断的是
nums[i]
与nums[i - 1]
是否重复,这样才能避免将三元组内的重复元素也给排除了这一问题
遇到的问题😢
尝试了哈希表写法,非常麻烦且容易漏细节,调了十分钟不行,直接去看了卡哥题解,后面使用双指针做出来了。
代码实现👨🏻💻
双指针法
var threeSum = function(nums) {
const res = [];
const len = nums.length;
// 排序数组
nums.sort((a, b) => a - b );
for(let i = 0; i < len; i++) {
let left = i + 1, right = len - 1,
if (nums[i] > 0) return res; // 非递减数组,第一个元素大于0,直接返回res
if (nums[i] == nums[i - 1]) continue; // 去重操作,避免将三元组内的重复元素也给排除了
while(left < right) {
let sum = nums[left] + nums[i] + nums[right];
if(sum < 0) left++; // 三数之和过小了,left右移
else if(sum > 0) right--; // 三数之和过大了,right左移
else {
res.push([nums[i], nums[left], nums[right]]); // 找到三数之和 = 0,向res中插入结果
while(left < right && nums[left] == nums[left + 1]) left++; // 去重,避免将三元组内的重复元素也给排除了
while(left < right && nums[right] == nums[right - 1]) right--; // 去重,避免将三元组内的重复元素也给排除了
left++; // 插入后继续移动left寻找下一个的三数之和
right--; // 插入后继续移动right寻找下一个的三数之和
}
}
}
return res;
};
总结📖
双指针法
LeetCode 18. 四数之和
题目链接🔗:18. 四数之和
解题思路🤔
整体思路与三数之和一样,不同之处在于要比三数之和多处理一次,两层for循环找到两个值,然后left
、right
指针遍历找到剩下两个值。
遇到的问题😢
代码实现👨🏻💻
双指针法
var fourSum = function(nums, target) {
const len = nums.length;
if(len < 4) return []; // 数组长度小于4,直接返回
nums.sort((a, b) => a - b); // 排序数组
const res = [];
for(let i = 0; i < len - 3; i++) { // 第一次缩小范围
if(i > 0 && nums[i] === nums[i - 1]) continue; // 去重
for(let j = i + 1; j < len - 2; j++) { // 第二次缩小范围
if(j > i + 1 && nums[j] === nums[j - 1]) continue; // 去重
let left = j + 1;
let right = len - 1;
while (left < right) {
const sum = nums[i] + nums[j] + nums[left] + nums[right];
if(sum < target) { // 四数之和小于target,移动left指针寻找更大的数
left++;
continue;
} else if (sum > target){ // 四数之和大于target,移动right指针寻找更小的数
right--;
continue;
} else {
res.push([nums[i], nums[j], nums[left], nums[right]]); // 向res中插入结果
// 去重
while (left < right && nums[left] === nums[left + 1]) left++;
while (left < right && nums[right] === nums[right - 1]) right--;
left++; // 插入成功后继续移动left寻找下一个的四数之和
right--; // 插入成功后继续移动left寻找下一个的四数之和
}
}
}
}
return res;
};
总结📖
双指针法
多了一次一层判断的情况下,也要记得对增加的判断进行去重。
今日收获
- 十分钟之内想不出来思路/改不出来代码就可以看题解了,没必要浪费太多时间钻牛角尖。一刷学思路,二刷搞巩固。
- 思路要灵活,不能学了哈希法忘了双指针。。。
- 感觉时间不太够用了,上班少摸鱼多做题!