我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第10篇文章 点击查看文章详情 🚀🚀
454. 四数相加 II - 力扣(LeetCode)
难度:🔥🔥🔥
Map实现
var fourSumCount = function(nums1, nums2, nums3, nums4) {
let resmap = new Map();
for(let i of nums1){
for(let j of nums2){
let sum = i + j;
resmap.set(sum,(resmap.get(sum) || 0) + 1);
}
}
let count = 0;
for(let i of nums3){
for(let j of nums4){
let sum = i + j;
count = count + (resmap.get(0-sum) || 0);
}
}
return count
};
难点
几乎所有的求和问题,都可以转化为求差问题
当然这道题也不例外
因为求差问题,都是两个数相减。
但这里有四个数,怎么办?
那我们就两两合并就好了~
大体思路如下:
- 首先定义一个
map,key放a和b两数之和,value放a和b两数之和出现的次数。 - 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到
map中。 - 创建变量
count,用来统计 a+b+c+d = 0 出现的次数。 - 在遍历大C和大D数组,找到如果
0-(c+d)在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。 - 最后返回统计值
count就可以了
为什么用map不用set?
其实也不难
map:是一组键值对的结构,和JSON对象类似。
set:类似于数组,且成员的值都是唯一的
而在这里,我们需要用到map中的键值对,所以只能用map
383. 赎金信 - 力扣(LeetCode)
难度:🔥
创建map对象
var canConstruct = function(ransomNote, magazine) {
if(ransomNote.length > magazine.length) return false;
let map = { }
for(const i of magazine){
map[i] = (map[i] || 0) + 1;
}
for(const i of ransomNote){
if(map[i]){
map[i]--;
if(map[i] < 0)
return false;
}
else{
return false
}
}
return true;
};
难点
哈哈哈哈,这个题没有难度了,和昨天的有效字母异位词差不多,第一遍就拿下了😁😁😁
15. 三数之和 - 力扣(LeetCode)
难度:🔥🔥🔥🔥
双指针实现
const threeSum = function(nums) {
// 用于存放结果数组
let res = [] // 给 nums 排序
nums = nums.sort((a,b)=>{ return a-b })
// 缓存数组长度
const len = nums.length
// 注意我们遍历到倒数第三个数就足够了,因为左右指针会遍历后面两个数
for(let i=0;i<len-2;i++) {
// 左指针j
let j=i+1
// 右指针k
let k=len-1
// 如果遇到重复的数字,则跳过
if(i>0&&nums[i]===nums[i-1]) {
continue
}
while(j<k) {
// 三数之和小于0,左指针前进
if(nums[i]+nums[j]+nums[k]<0){
j++
// 处理左指针元素重复的情况
while(j<k&&nums[j]===nums[j-1]) { j++ }
}
else if(nums[i]+nums[j]+nums[k]>0){
// 三数之和大于0,右指针后退 k--
// 处理右指针元素重复的情况
while(j<k&&nums[k]===nums[k+1]) { k-- }
}
else {
// 得到目标数字组合,推入结果数组
res.push([nums[i],nums[j],nums[k]])
// 左右指针一起前进
j++
k--
// 若左指针元素重复,跳过
while(j<k&&nums[j]===nums[j-1]) { j++ }
// 若右指针元素重复,跳过 while(j<k&&nums[k]===nums[k+1]) { k-- }
}
}
}
// 返回结果数组
return res
};
难点
实现思路
修言老师讲解肯定比我好得多,所以直接上大佬的吧~
三数之和延续两数之和的思路,我们可以把求和问题变成求差问题——固定其中一个数,在剩下的数中寻找是否有两个数和这个固定数相加是等于0的。
虽然乍一看似乎还是需要三层循环才能解决的样子,不过现在我们有了双指针法,定位效率将会被大大提升,从此告别过度循环~
(这里大家相信已经能察觉出来双指针法的使用场景了,一方面,它可以做到空间换时间;另一方面,它也可以帮我们降低问题的复杂度。)
双指针法用在涉及求和、比大小类的数组题目里时,大前提往往是:该数组必须有序。否则双指针根本无法帮助我们缩小定位的范围,压根没有意义。因此这道题的第一步是将数组排序:
nums = nums.sort((a,b)=>{ return a-b })
然后,对数组进行遍历,每次遍历到哪个数字,就固定哪个数字。然后把左指针指向该数字后面一个坑里的数字,把右指针指向数组末尾,让左右指针从起点开始,向中间前进:
每次指针移动一次位置,就计算一下两个指针指向数字之和加上固定的那个数之后,是否等于0。如果是,那么我们就得到了一个目标组合;否则,分两种情况来看:
- 相加之和大于0,说明右侧的数偏大了,右指针左移
- 相加之和小于0,说明左侧的数偏小了,左指针右移
tips:这个数组在题目中要求了“不重复的三元组”,因此我们还需要做一个重复元素的跳过处理。这一点在编码实现环节大家会注意到。
为什么要先排序
因为我们的思路是,通过移动两个双指针,判断它们三者的值是否满足条件,如果两个指针区间内值是无序的,我们就不能进行如下操作:
- 三者相加之和大于0,说明右侧的数偏大了,右指针左移
- 三者相加之和小于0,说明左侧的数偏小了,左指针右移
剪枝+去重
- 剪枝:为什么
nums[i]===nums[i-1],而不是nums[i]===nums[i+1]?首先这里的
i是我们要固定的那个数,在那个数后面紧挨着的就是双指针的left了。如果用了
nums[i]===nums[i+1]的话,[-1,-1,2]这个答案,就会当i等于第一个-1的时候,直接因为第二个数还是-1就舍去了。 - 去重:在循环中,对每种情况都要进行判断
while(j<k) {
// 三数之和小于0,左指针前进
if(nums[i]+nums[j]+nums[k]<0){
j++
// 处理左指针元素重复的情况
while(j<k&&nums[j]===nums[j-1]) { j++ }
}
else if(nums[i]+nums[j]+nums[k]>0){
// 三数之和大于0,右指针后退 k--
// 处理右指针元素重复的情况
while(j<k&&nums[k]===nums[k+1]) { k-- }
}
else {
// 得到目标数字组合,推入结果数组
res.push([nums[i],nums[j],nums[k]])
// 左右指针一起前进
j++
k--
// 若左指针元素重复,跳过
while(j<k&&nums[j]===nums[j-1]) { j++ }
// 若右指针元素重复,跳过 while(j<k&&nums[k]===nums[k+1]) { k-- }
}
}
18. 四数之和 - 力扣(LeetCode)
难度:🔥🔥🔥🔥
双指针
var fourSum = function(nums, target) {
let len = nums.length;
let res = []
nums.sort((a,b) => a - b)
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 l = j + 1;
let r = len-1;
while(l < r){
let sum = nums[i] + nums[j] + nums[l] + nums[r];
if(sum > target){
r--;
while(l < r && nums[r] == nums[r+1]){
r--
}
}
else if(sum < target){
l++;
while( l < r && nums[l] == nums[l-1]){
l++;
}
}
else{
res.push([nums[i],nums[j],nums[l],nums[r]])
l++
r--;
while((l < r) && nums[r] == nums[r+1]){
r--;
}
while(l < r && nums[l] == nums[l-1]){
l++;
}
}
}
}
}
return res;
};
这里和三数之和的思路基本上一样,唯一的区别就是要再多一层循环。
将i和j的和合并,对后面两个数再使用双指针.
收获
今天的题目比昨天的要难一些,但收获也更多!
- 四数相加:两两合并的思路挺有意思的,以及熟悉了一下
map这个数据结构的apimap.set
map.get
- 赎金信:和昨天的题一样,收获已经在昨天完成了~
- 三数之和:不愧是经典题型
先排序再进行双指针的移动
怎么样剪枝和去重
- 四数之和:是三数之和的升级版,但也就多了一层for循环而已~
⭐⭐⭐