代码随想录day7| 454.四数相加II 、383. 赎金信、15. 三数之和、 18. 四数之和

72 阅读3分钟

454.四数相加II

image.png

思路:nums1和nums2组成一个哈希表map1,key和value分别是两数之和,出现的次数 num3和num4也是如此,组成map2,在map2中寻找值为 0 - key(map1) 并将两个vulue相乘,累加得到最终结果

优化: map2无需进行存储,而是在遍历num3,num4时直接进行与map1中的key进行比较相加

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @param {number[]} nums3
 * @param {number[]} nums4
 * @return {number}
 */
var fourSumCount = function (nums1, nums2, nums3, nums4) {
    // 将 nums1和nums2 的两数之和 存储一个map,将 nums3和nums4 存储一个map,key为两数之和,value为出现次数
    // 再遍历map1 ,在map2中寻找是否有 对应相加为 0 的,将两个value 相乘,最后所有相加即为结果
    let map1 = new Map()
    let map2 = new Map()
    let sum = 0
    nums1.forEach(item1 => {
        nums2.forEach(item2 => {
            const sum = item1 + item2
            map1.set(sum, map1.has(sum) ? map1.get(sum) + 1 : 1)
        })
    })
    nums3.forEach(item1 => {
        nums4.forEach(item2 => {
            const sum = item1 + item2
            map2.set(sum, map2.has(sum) ?map2.get(sum) + 1 : 1)
        })
    })
    map1.forEach((value, key)=>{
        const diff = 0 - key
        if(map2.has(diff)){
            sum += value * map2.get(diff)
        }
    })
    return sum
}
    // 改进,数组3,4的两数之和不需要在存储了,得到结果的时候进行sum+1就可以
var fourSumCount = function (nums1, nums2, nums3, nums4){
    let map1 = new Map()
    let res = 0
    nums1.forEach(item1 => {
        nums2.forEach(item2 => {
            const sum = item1 + item2
            map1.set(sum, map1.has(sum) ? map1.get(sum) + 1 : 1)
        })
    })
    nums3.forEach(item1 => {
        nums4.forEach(item2 => {
            const sum = item1 + item2
            res+=( map1.get( 0 -sum) || 0 )
        })
    })

    return res
};

383. 赎金信

image.png

思路:将magazine字母作为key,出现的次数作为value存储为map,遍历ransomNote, 对map相应的key的value--,当value的值小于0,则false

改进: 字母一共只有26个,可以指定一个长度为26的数组,存储magazine中每个字母出现的顺序 遍历ransomNote时更改value

在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!


/**
 * @param {string} ransomNote
 * @param {string} magazine
 * @return {boolean}
 */
var canConstruct = function(ransomNote, magazine) {
    if(ransomNote.length > magazine.length) return false
    const record = new Array(26).fill(0)
    const base = 'a'.charCodeAt()
    for(let i = 0;i<magazine.length;i++){
        record[magazine[i].charCodeAt() - base]++
    }
    for(let i = 0; i<ransomNote.length; i++){
        record[ransomNote[i].charCodeAt() - base]--
        if(record[ransomNote[i].charCodeAt() - base] < 0){
            return false
        }
    }
    return true
};

15. 三数之和

image.png 思路; 因为不需要返回下标,进行排序后,在for循环中通过双指针遍历,可以将O(n33)变成(n22)

不用哈希表的原因:
通过两层for循环可以确定a,b然后通过哈希法来确定 0-(a+b) 是否在 数组里出现过,这里是没问题的,但是不能返回重复的集合,去重是非常麻烦的

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function (nums) {
    const res = [], len = nums.length
    // 将数组排序
    nums.sort((a, b) => a - b)
    for (let i = 0; i < nums.length -2; i++) {
        let l = i + 1, r = nums.length - 1, iNum = nums[i]
        if (iNum > 0) return res
        // 这里是对i的去重
        if (iNum === nums[i - 1]) continue
        while (l < r) {
            let lNum = nums[l], rNum = nums[r], threeSum = iNum + lNum + rNum
            if (threeSum > 0) {
                r--
            } else if (threeSum < 0) {
                l++
            } else {
                res.push([iNum, lNum, rNum])
                // 对l去重
                while (l < r && nums[l] === nums[l + 1]) {
                    l++
                }
                // 对r去重
                while (l < r && nums[r] === nums[r - 1]) {
                    r--
                }
                l++
                r--
            }
        }
    }
    return res
};

18. 四数之和

image.png

思路和三数之和一样,多一层for循环
需要注意的是,j > i+1的时候才进行比较去重,要保证每次i循环的时候第一个j可以执行循环

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function(nums, target) {
    let res = []
    nums.sort((a,b)=>a-b)
    // 这还不能这么写,因为全是负的,越加越小
    // if(nums[0] > target) return res
    for(let i = 0;i<nums.length -3;i++){
        let j = i+1
        if(nums[i] === nums[i-1]) continue
        for(;j<nums.length;j++){
            // 这里要增加j>1的条件,确保每次i循环的时候,j第一次能执行
            if(j > i+1 && nums[j] === nums[j-1]) continue
            let l = j+1,r=nums.length -1, jNum = nums[j]
            while(l<r){
                let fourSum = nums[i] + jNum + nums[l] + nums[r]
                if(fourSum < target){
                    l++
                }else if(fourSum > target){
                    r--
                }else{
                    res.push([nums[i] , jNum , 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 res
};