代码随想录算法训练营第七天 | 454.四数相加 II、383. 赎金信、15. 三数之和、18.四数之和

87 阅读2分钟

454.四数相加II

383.赎金信

15.三数之和

18.四数之和

454. 四数相加II

  • 我的思路:对于 n1+n2+n3+n4=0 有3种组合情况,n1=-n2 && n3=-n4 etc,可以遍历数组运用哈希表记录出现的情况来做。(但好像有点复杂了...
  • 误区:1. 没有把重点放在可重复;2. 多种组合情况只是解法组合,不用都考虑;3. 没有想明白用哈希表记录什么。
  • 正解:先找两个数组的和并记录其次数,再遍历另外两个数组来匹配。
var fourSumCount = function(nums1, nums2, nums3, nums4) {
    const map = new Map();
    for (let i=0; i<nums1.length; i++) {
        for (let j=0; j<nums2.length; j++) {
            const sum = nums1[i] + nums2[j];
            if (map.has(sum)) {
                map.set(sum, map.get(sum) + 1);
            } else {
                map.set(sum, 1);
            }
        }
    }

    let res = 0;
    for (let i=0; i<nums3.length; i++) {
        for (let j=0; j<nums4.length; j++) {
            const sum = nums3[i] + nums4[j];
            if (map.has(-sum)) {
                res += map.get(-sum);
            }
        }
    }

    return res;
};

383. 赎金信

var canConstruct = function(ransomNote, magazine) {
    const arr = new Array(26).fill(0);

    for (const r of ransomNote) {
        arr[r.charCodeAt() - 'a'.charCodeAt()] += 1;
    }

    for (const m of magazine) {
        const idx = m.charCodeAt() - 'a'.charCodeAt();
        if (arr[idx] !== 0) {
            arr[idx] -= 1;
        }
    }

    let res = true;
    for (const a of arr) {
        if (a !== 0) return false;
    }
    return res;
};

15. 三数之和

  • 我的思路:三指针递归找满足条件的数组
  • 缺陷:1. 去重的方法很笨,果然超时;2. 想到了排序,但没有灵活运用到排序带来的好处。
  • 正解:1. 排序后双指针可根据相对大小调整位置;2. 对每个组成元素考虑去重
var threeSum = function(nums) {
    let res = [];
    nums.sort((a,b) => a-b);

    for (let i=0; i<nums.length; i++) {
        if (nums[i] > 0)    return res;
        // 对a去重
        if (i>0 && nums[i] === nums[i-1])   continue;

        let l = i+1;
        let r = nums.length-1;
        while(l < r) {
            if (nums[i] + nums[l] + nums[r] > 0)    r--;
            else if (nums[i] + nums[l] + nums[r] < 0)   l++;
            else {
                res.push([nums[i], nums[l], nums[r]]);
                // 对b、c去重
                while(r > l && nums[r] === nums[r-1])   r--;
                while(l < r && nums[l] === nums[l+1])   l++;
                // 开启下一轮匹配
                r--;
                l++;
            }
        }
    }

    return res;
};

18.四数之和

  • 我的思路:排序后a和d作为最外圈包围,b和d作为内层区间不断收缩和去重找解
  • 缺陷:a和d一起收缩,会遗漏掉a不变而需要d变的场景
  • 正解:固定a,移动b,以b为可变区间起点移动c和d。** 剪枝时注意只剪正数情况
var fourSum = function(nums, target) {
    if (nums.length < 4)    return [];

    nums.sort((a,b) => a-b);
    const res = [];

    for (let i=0; i<=nums.length-1; i++) {
        if (nums[i] >= 0 && nums[i] > target)   break;
        if (i > 0 && nums[i] === nums[i-1]) continue;

        for (let k = i+1; k<nums.length-1; k++) {
            if (nums[i] + nums[k] > target && nums[i] + nums[k] >= 0) break;

            if (k > (i+1) && nums[k] === nums[k-1]) continue;

            let l = k+1, r = nums.length-1;
            while (l < r) {
                const sum = nums[i] + nums[k] + nums[l] + nums[r];
                if (sum > target) {
                    r--;
                } else if (sum < target) {
                    l++;
                } else {
                    res.push([nums[i], nums[k], nums[l], nums[r]]);

                    while(l<r && nums[r] === nums[r-1]) r--;
                    while(l<r && nums[l] === nums[l+1]) l++;

                    r--;
                    l++;
                }
            }
        }
    }
    
    return res;
};