代码随想录算法营DAY7 | 454.四数相加II 383. 赎金信 15. 三数之和 18. 四数之和
454.四数相加II
题目链接: 454.四数相加II
思路:
1.根据两数相加的思路拓展,把A[i] + B[j] + C[k] + D[l] = 0看作(A[i] + B[j]) + (C[k] + D[l]) = 0。
2.两个for循环遍历A,B;先用map把A[i] + B[j] 的结果作为key存储,和出现的次数作为value
3.两个for循环遍历C,D;检查map里是否有(0-( C[k] + D[l] ))的元素,如果有,
代码:
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加上a+b的和出现的次数
}
}
}
return count;
};
问题:
这里的 map.set(i + j, (map.get(i + j) || 0) + 1)表达式是什么意思?
复杂度分析:
时间复杂度:
O(n^2)
383. 赎金信
题目链接: 383. 赎金信
思路:
1.因为全是小写字母,所以可以用数组映射,这里用map空间开销比较大,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
2.遍历magazine统计字母出现频率。
3.遍历ransomNote,若ransomNote里的字母在magazine里面没出现过就就直接返回false,若遍历完了没返回false,则可以返回true。
代码:
var canConstruct = function(ransomNote, magazine) {
const strArr = new Array(26).fill(0),
base = "a".charCodeAt();
for(const s of magazine){
strArr[s.charCodeAt() - base]++;
}
for(const s of ransomNote){
const index = s.charCodeAt()-base;
if(!strArr[index]) return false;
strArr[index]--;
}
return true;
};
复杂度分析:
时间复杂度:O(n)
15. 三数之和
题目链接: 15. 三数之和
思路:
- 首先排序数组为非递减数组,然后定义指针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 = [], len = nums.length
// 将数组排序
nums.sort((a, b) => a - b)
for (let i = 0; i < len; i++) {
let l = i + 1, r = len - 1, iNum = nums[i]
// 数组排过序,如果第一个数大于0直接返回res
if (iNum > 0) return res
// 去重
if (iNum == nums[i - 1]) continue
while(l < r) {
let lNum = nums[l], rNum = nums[r], threeSum = iNum + lNum + rNum
// 三数之和小于0,则左指针向右移动
if (threeSum < 0) l++
else if (threeSum > 0) r--
else {
res.push([iNum, lNum, rNum])
// 去重
while(l < r && nums[l] == nums[l + 1]){
l++
}
while(l < r && nums[r] == nums[r - 1]) {
r--
}
l++
r--
}
}
}
return res
};
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;
};
感想总结
js基础还需要巩固,学完js估计还得回来再刷一遍555