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

90 阅读3分钟

1. 两数之和

1. 两数之和 找数组内,两个元素值的和=target的数的下标 image.pngMap来存遍历过的key: 元素值``value: 下标,遍历数组,计算它和target的差,找Map中有没有:

  • 有:返回下标
  • 没有:加入Map中继续找

image.png image.png

失散的母子们:一群人中有几对母子,警察一个一个问他们要找的谁,用一张表存每个人的信息和要找的人,首先看表里有没有要找的那个人,如果没有就把他要找的人和信息记下来,看后面会不会有人找

map映射

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) { // 使用map集合key:值,value:下标
    let map = new Map()
    for (let i = 0; i < nums.length; i++) { // 遍历数组
        let sub = target - nums[i] // 计算满足条件的差值
        if (map.has(sub)) return [map.get(sub), i] // 如果之前遍历过的有,返回下标
        else {
            map.set(nums[i], i) // 没有就加入set(k, v)存入map
        }
    }
    return []
};

454. 四数相加 II

454. 四数相加 II 四个独立的数组,求有几个A[i] + B[j] + C[k] + D[l] = 0,即a + b + c + d = 0

  1. 定义一个mapkey: a+b,value: a+b出现过几次
  2. 遍历A和B,统计两数组元素之和,出现次数,存入map
  3. 定义count,用来统计a+b+c+d = 0出现的次数
  4. 遍历C和D,map中找有0-(c+d)吗?有:**count**加上出现次数

image.png

Set

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @param {number[]} nums3
 * @param {number[]} nums4
 * @return {number}
 */
var fourSumCount = function(nums1, nums2, nums3, nums4) {
  // 类似与两数相加,用map存
    let map = new Map() //key为a+b,value为出现次数
    for (let a of nums1) { 
        for (let b of nums2) {
            let sum = a + b //先求a+b放入map
            map.set(sum, (map.get(sum) || 0) + 1) //如果之前没有0+1变为1,有就取值+1
        }
    }
    let count = 0 // 存最终结果
    for (let c of nums3) {
        for (let d of nums4) {
            let sub = 0 - (c+d) //求让和为0的
            if(map.has(sub)) //在map中
             count = count + map.get(sub) //个数+出现次数
        }
    }
    return count
};

383. 赎金信

383. 赎金信 后一个字符串中能找到前一个字符串中的所有元素

用数组映射

/**
 * @param {string} ransomNote
 * @param {string} magazine
 * @return {boolean}
 */
var canConstruct = function(ransomNote, magazine) {
    let hash = new Array(26).fill(0) //哈希映射26个字母
    let base = "a".charCodeAt()
    for (let i = 0; i < ransomNote.length; i++) { //r数组有的++
        hash[ransomNote[i].charCodeAt() - base]++
    }
    for (let j = 0; j < magazine.length; j++) { //m数组有的--
        hash[magazine[j].charCodeAt() - base]--
    }
    for (let h of hash) { //最后如果还有>0的数,说明存在r数组中有,m数组中没有的
        if (h > 0) return false 
    }
    return true
};

15. 三数之和

15. 三数之和 在一个数组内,找三个数的和为0,形成的不能重复的数组 用双指针解题:a + b + c = 0 image.png

  1. 首先对数组进行排序(小->大)
  2. i去遍历数组(a),li下一个(b),r从最后一个(c),向中间移动找到合适的
  3. 可以用-1 -1 0 0 1 1 3判断去重
  4. 判断去重要找与前一个是否相等,相等说明这个数的已经处理过了,再处理也是一样的重复
  • 注意:不能先去重再找,会少一些情况:比如[0, 0, 0]就直接被删除了

三指针剪枝

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    nums.sort((a, b) => a-b) // 按照升序对数组排序
    let result = [], conut = 0 //存最后得到的数组
    for (let i = 0; i < nums.length; i++) { // i去遍历数组
        // 如果排序后第一个元素都>0,再与后面元素相加怎么也不可能为0
        if (nums[i] > 0) break 
        // a去重,如果前面已经有了说明重复了
        if (i > 0 && nums[i] === nums[i-1]) continue
        let l = i + 1 //l从i下一个
        let r = nums.length - 1 //r从最后一个
        while (l < r) { //循环找让a+b+c = 0
            if (nums[i] + nums[l] + nums[r] > 0) r-- //>0 r前移让和变小
            else if (nums[i] + nums[l] + nums[r] < 0) l++ //<0 l后移让和变大
            else{ //找到等于0
                result[conut++] = [nums[i], nums[l], nums[r]] //加入结果数组

                // 对l,r去重,移一位
                while (nums[l + 1] === nums[l]) l++
              	// 如果下一个相同就跳过,找下一个不相同的
                while (nums[r - 1] === nums[r]) r--
                
                // 找到答案时,双指针同时收缩
                l++  // 跳到不相同的下一个
                r--
            } 
        }
    }
    return result
};

18. 四数之和

18. 四数之和 三数之和的升级版 image.png

  • 四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,一样是l``r移动找和满足target,五数之和、六数之和等等都采用这种解法
  • 剪枝的时候要用break直接结束循环(后面都不看了),不能直接returnnums[i] > target && nums[i] >=0比如:数组是[-4, -3, -2, -1],target是-10,不能因为-4 > -10而跳过,看nums[i]是否>=0,如果>=0,说明后面比它大的都是正数,加上正数只会越来越大
  • 去重:与前一个相等,直接结束这次,继续下次循环continue(这个数不用看了)
  1. 排序:从小到大
  2. 遍历前面的值
  3. 剪枝,排除不满足的情况:已经大于target>=0后面值越来越大、和前面相同:必须加上>起始值限制i > k + 1、k > 0
  4. l, r分别从剩下的往中间遍历

四指针剪枝

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function(nums, target) {
     let result = [], count = 0 // 存
     nums.sort((a, b) => a - b) // 排序
     for (let k = 0; k < nums.length; k++) { //k遍历数组
        // 剪枝:排除不满足的情况
        //一级剪枝
         if (nums[k] > target && nums[k] >= 0 ) break // 不可能==target
         if (k > 0 && nums[k] === nums[k - 1]) continue // k去重
         for (let i = k + 1; i < nums.length; i++) { //i从k+1
             let l = i + 1
             let r = nums.length - 1
             let sum = nums[k] + nums[i] // 已确定的k,i和
           //二级剪枝
             if(sum > target && sum >= 0) break
             // 第一次i = K + 1时不能和前面比较剪枝i > K + 1
             if (i > k + 1 && nums[i] === nums[i - 1]) continue

             while (l < r) {
                 if (sum + nums[l] + nums[r] > target) r--
                 else if (sum +nums[l] + nums[r] < target) l++
                 else {
                     result[count++] = [nums[k], nums[i], nums[l], nums[r]]
                     
                     while (l < r && nums[l + 1] === nums[l]) l++
                     while (l < r && nums[r - 1] === nums[r]) r--

                     l++
                     r--
                 }
             }
         }
     }
     return result
};