454.四数相加II
题目链接:454. 四数相加 II - 力扣(LeetCode)
第一想法
用map
试过的四个数就放在集合之中,并且是key:value的形式
这样可以避免重复,也可以根据值找到索引
但是怎么从这四个数组里面选出四个数,暂时没什么头绪
思路
还是类似于1.两数之和这一题的思路
构造一个集合,循环遍历查找集合中满足条件的元素
步骤如下:
- 遍历a,b数组,将其中的两个数相加
- 创建一个map,key为两数相加和,value为和出现的次数
- 遍历c,d数组,从集合中找key为(0-c-d)的值,如果有,则返回出现次数
- 如果没有,说明这四个数组中不存在满足条件的四个值
JS代码如下:
var fourSumCount = function(nums1, nums2, nums3, nums4) {
let map = new Map();
let num = 0;
// 把数组1和2的和存储到map中
for(let i = 0; i < nums1.length; i++){
for(let j = 0; j < nums2.length; j++){
let sum = nums1[i] + nums2[j];
let value = 1;
if(map.has(sum)){
value += map.get(sum);
}
map.set(sum,value)
}
}
// 在数组3和4中遍历,寻找满足条件的情况
for(let i = 0; i < nums3.length; i++){
for(let j = 0; j < nums4.length; j++){
let sum = nums3[i] + nums4[j];
if(map.has(0-sum)){
num += map.get(0-sum);
}
}
}
return num;
};
总结
这题的大体思路和两数之和很像
不过需要想想怎么构造出一个集合,再从集合中找满足条件的值
一开始以为需要返回的是四个下标,结果题目问的实际上是符合条件的数量
这种情况的话,哈希表的方法非常适合了
另外,需要注意一些细节
比如第一次提交时,满足条件时我写的是num += 1
这就会出现报错了
实际上程序可以更精简一点,if判断可以被替代:
for(const n1 of nums1) {
for(const n2 of nums2) {
const sum = n1 + n2;
twoSumMap.set(sum, (twoSumMap.get(sum) || 0) + 1)
}
}
383. 赎金信
第一想法
和242一样的,构造一个存储字母出现次数的哈希表
思路
构建一个哈希表,存储第二个单词出现的字母的次数
遍历第一个单词,把每个字母对应于哈希表上的位置的值减1
如果第一个字母可以由第二个字母组成
那么遍历结束后,数组中不可能有负数
所以,可以用数组中是否有负数来判断是否符合条件
JS代码如下:
var canConstruct = function(ransomNote, magazine) {
let arr = new Array(26).fill(0);
let mode = 'a'.charCodeAt();
for(let i of magazine){
let n = i.charCodeAt() - mode;
arr[n]++;
}
for(let i of ransomNote){
let n = i.charCodeAt() - mode;
arr[n]--;
}
for(let i = 0; i < 26; i++){
if(arr[i] < 0){
return false;
}
}
return true;
};
总结
我这里用了三次循环遍历,其实可以简单一些,在第二次循环遍历时就做逻辑判断
for(const s of ransomNote) { // 对应的字符个数做--操作
const index = s.charCodeAt() - base;
if(!strArr[index]) return false; // 如果没记录过直接返回false
strArr[index]--;
}
return true;
如果第一个单词中有出现哈希表中没有记录(值为0)的字母,则直接返回false
另外,这里用map看上去会更方便一些
但是,在c++情况下:
其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
JS中,一般情况下,数组比map更节约空间,因为不需要存储键值对。
如果要快速查找或者更新元素,map会更高效
另外,因为JS中会自动分配和释放内存,所以不需要太关注内存管理
15. 三数之和
第一想法
遍历一遍统计所有a+b的情况
map的key可以是对象,让key为一个二元数组,存储元素的index
再遍历一遍数组,找到满足a+b+c ===0 的元素,将元素index与map中的key对比,是否重复
如果没有重复,感觉元素index返回符合条件的三个数
这样做,复杂度有点太高了
而且使用map也比较复杂
思路
双指针法:
- 将数组升序排列
- i指向第一个元素,并且使用一个for循环
- left指向i的下一个元素,right指向最后一个元素
- 根据目前三数之后的情况移动指针
tips:
题目说了必须是不重复的三元数组,那什么时候会有重复的情况呢?
JS代码如下:
var threeSum = function(nums) {
nums.sort((a,b) => a - b);
let result = [];
for(let i = 0; i < nums.length; i++){
if(nums[i] > 0){
return result;
}
//对i去重
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
let left = i + 1;
let right = nums.length - 1;
while(left < right){
if(nums[i]+nums[left]+nums[right] > 0){
right--;
}
else if(nums[i]+nums[left]+nums[right] < 0){
left++;
}else{
result.push([nums[i],nums[left],nums[right]]);
// 左右去重
while(left < right && nums[right - 1] == nums[right]){
right--;
}
while(left < right && nums[left + 1] == nums[left]){
left++;
}
left++;
right--;
}
}
}
return result;
};
总结
找三个数等于
对一个数进行循环,另外两个数用双指针
其实把数组转化为有序的,这题就和数组的双指针的题目非常像了
i和左右指针的去重很重要
也需要注意,写题目的过程中因为去重边界的处理,出了两次bug
while(left < right && nums[right - 1] == nums[right]){
right--;
}
这里应该用while,而不是if
18. 四数之和
第一想法
和454有一点像,不过454返回的是满足条件的数量
这一题需要返回满足条件的元组,而且互不重复
创建map构建哈希表的话,有点太复杂了
这一题应该还是双指针解法
比15多套一层for循环
思路
两层循环
注意每层循环的break条件以及去重处理
JS代码如下:
var fourSum = function(nums, target) {
nums.sort((a,b) => a-b)
let res = [];
let len = nums.length;
for(let i = 0; i < len-1; i++){
if(nums[i] > target && target > 0){
break;
}
// 去重处理
if(nums[i] == nums[i-1] && i > 0){
continue;
}
for(let j = i+1; j < len; j++){
if(nums[i] + nums[j] > target && target > 0){
break;
}
// 去重处理
if(nums[j] == nums[j-1] && j > i+1){
continue;
}
let l = j + 1;
let r = len - 1;
while(l < r){
if(nums[i]+nums[j]+nums[l]+nums[r] > target){
r--;
}else if(nums[i]+nums[j]+nums[l]+nums[r] < target){
l++;
}else{
res.push([nums[i],nums[j],nums[l],nums[r]]);
// 去重处理
while(nums[r-1] == nums[r]){
r--;
}
while(nums[l+1] == nums[l]){
l++;
}
l++;
r--;
}
}
}
}
return res;
};
总结
这一题思路和上一题是一样的
但是细节有很多,必须注意
比如,
if(nums[j] == nums[j-1] && j > i+1){
continue;
}
这里j > i+1这个一定不能漏了!
res直接在最后一起返回,防止混乱。