提示:努力生活,开心、快乐的一天
文章目录
454.四数相加II
💡解题思路
- 使用哈希表中的map,
遍历大A和大B数组,key放a和b两数之和,value 放a和b两数之和出现的次数
遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来
🤔遇到的问题
- map中存的value(放a和b两数之和出现的次数)时,默认存1
- map中找到的符合条件的key(放a和b两数之和),取出其value,加入count中
💻代码实现
哈希表-map
var fourSumCount = function (nums1, nums2, nums3, nums4) {
//哈希表-map
let ABSumMap = new Map();
//四数相加为0 的个数
let count = 0;
//先遍历nums1, nums2
//将其和sum放进map的key值
//对应的value值为sum和出现的次数
for (n1 of nums1) {
for (n2 of nums2) {
const sum = n1 + n2;
ABSumMap.set(sum,(ABSumMap.get(sum)||0)+1)
}
}
//再遍历nums3, nums4
//在ABSumMap查找的目标值target,就是0-(n3+n4)
//target存在于map的key中,就说明此时的四个数和为0
//map中key对应的value,就是是多少个,就需要count+=value
for (n3 of nums3) {
for (n4 of nums4) {
const sum = n3 + n4;
const target = 0 - sum;
count+=(ABSumMap.get(target)||0)
}
}
return count
};
🎯题目总结
- 这道题目是四个独立的数组,只要找到
A[i] + B[j] + C[k] + D[l] = 0
就可以,不用考虑有重复的四个元素相加等于0的情况,如果不重复,就不能在用哈希表
383. 赎金信
💡解题思路
- 依旧是26个字母,应该用
哈希表中的数组
两个字符串,将父字符串存入数组,子字符串与数组相应位置作对比
🤔遇到的问题
暂无,有时间可用map和set进行实现
💻代码实现
哈希表-数组
var canConstruct = function(ransomNote, magazine) {
//采用哈希表中的数组
let strarr = new Array(26).fill(0)
let base = 'a'.charCodeAt()
//先遍历父字符串,每个字符存在指定位置,重复的字符+1
for(let i of magazine){
strarr[i.charCodeAt()-base]++
}
//后遍历子字符串,每个字符与数组中相对应的位置作比较
//该位置如果是0,直接返回false
//否则,该位置的数字-1
for(let j of ransomNote){
let index = j.charCodeAt()-base
if(!strarr[index]) return false
strarr[index]--
}
return true
};
🎯题目总结
经典的哈希题目,采用了数组的模式进行,与之前的经典哈希题 ,基本一致
15. 三数之和
💡解题思路
- 不适合使用哈希表,此处适合
双指针+排序
- 动画图示:https://code-thinking.cdn.bcebos.com/gifs/15.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.gif
- 首先将
数组排序
,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上,这里相当于a = nums[i],b = nums[left],c = nums[right]
- nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以
right下标就应该向左移动
,这样才能让三数之和小一些 - nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,
left 就向右移动
,才能让三数之和大一些,直到left与right相遇为止 - 去重逻辑,i,right,left 都需要去重;i去重:
nums[i] == nums[i - 1]
;b去重:right去重:nums[right] == nums[right - 1]
;left去重:nums[left] == nums[left + 1]
🤔遇到的问题
- sum值的位置,应该在left移动的循环中,而不是在外层for循环中
- b,c去重时候导致right溢出
💻代码实现
双指针+排序
var threeSum = function (nums) {
//双指针
let res = []
let len = nums.length;
//给数组排序
nums.sort((a, b) => a - b)
for (let i = 0; i < len; i++) {
//排过序,如果i值大于0,直接结束,返回res
if (nums[i] > 0) return res
//左指针
let left = i + 1;
//右指针
let right = len - 1;
//i去重
if (nums[i] == nums[i - 1]) continue
while (left < right) {
let sum = nums[i] + nums[left] + nums[right];
if (sum > 0) {
right--
} else if (sum < 0) {
left++
} else {
res.push([nums[i], nums[left], nums[right]])
//left去重
while (left < right && nums[left] == nums[left + 1]) {
left++
}
//right去重
while (left < right && nums[right] == nums[right - 1]) {
right--
}
//移动left和right指针
left++;
right--;
}
}
}
return res
};
🎯题目总结
排序以后,原来的下标就失去啦,如果此题需要返回下标,那一定不能用该方法
- 是判断 nums[i] 与 nums[i + 1]是否相同,还是判断 nums[i] 与 nums[i-1] 是否相同?我们要做的是 不能有重复的三元组,但三元组内的元素是可以重复的!这里是有两个重复的维度
- left和righ去重需要注意left<right
18. 四数之和
💡解题思路
- 与三数之和基本一致,只是在
最外层再添加一层for循环
,另外去重剪枝条件有所不同 前4个数和>target,后面的数越来越大,直接跳出
第一个数+最后面三个数(数组中最大的数)< target,后面的数越来越小,直接跳出
🤔遇到的问题
- 第一个和第二个数字的去重剪枝操作问题
💻代码实现
双指针+排序
var fourSum = function(nums, target) {
const res = []
let len = nums.length;
if(len<4) return res;
nums.sort((a,b)=>a-b);
//新增的一层循环,第一个数字,排序后最小的数字
for(let k=0;k<len-3;k++){
//去重操作
if(k>0&&nums[k]==nums[k-1]) continue;
//前4个数已经大于target,后面的数越来越大,直接结束
if(nums[k]+nums[k+1]+nums[k+2]+nums[k+3]>target) break
//第一个数+最后面三个数(数组中最大的数)还小于target,后面怎么加sum也小于targe
if(nums[k]+nums[len-1]+nums[len-2]+nums[len-3]<target) continue
//与三数之类的步骤基本相同,遍历第三个数
for(let i = k+1;i<len-2;i++){
//去重,与上面的相同
if(i>k+1&&nums[i]==nums[i-1]) continue
if(nums[k]+nums[i]+nums[i+1]+nums[i+2]>target) break
if(nums[k]+nums[i]+nums[len-1]+nums[len-2]<target) continue
//left和right的去重与移动,与三数之和相同
let left = i+1;
let right = len-1;
while(left<right){
//sum是四个数相加
const sum = nums[k]+nums[i]+nums[left]+nums[right]
if(sum>target){
right--
}else if(sum<target){
left++
}else{
res.push([nums[k],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--;
}
}
}
}
return res
};
🎯题目总结
四数之和是在三数之和的额基础上再加一层for循环,另外需要注意去重和剪枝,那么之后的五数之和等等,都可以用该方法,该方法对比暴力破解降低了复杂度,此题就是从O(n^4)降为 O(n^3)
🎈今日心得
双指针与排序相结合,很奇妙,相同题目,一点点的限制条件更改,可能就需要修改整体的逻辑甚至需要更换结题模式,还不错努力完成啦