哈希表篇:454.四数相加II、 383. 赎金信、15. 三数之和、18. 四数之和

提示:努力生活,开心、快乐的一天


454.四数相加II

题目链接: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. 赎金信

题目链接: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. 三数之和

题目链接: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. 四数之和

题目链接:18. 四数之和

💡解题思路

  1. 与三数之和基本一致,只是在最外层再添加一层for循环,另外去重剪枝条件有所不同
  2. 前4个数和>target,后面的数越来越大,直接跳出
  3. 第一个数+最后面三个数(数组中最大的数)< target,后面的数越来越小,直接跳出

🤔遇到的问题

  1. 第一个和第二个数字的去重剪枝操作问题

💻代码实现

双指针+排序

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)

🎈今日心得

双指针与排序相结合,很奇妙,相同题目,一点点的限制条件更改,可能就需要修改整体的逻辑甚至需要更换结题模式,还不错努力完成啦

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值