2Sum 3Sum 4Sum 问题

摘自labuladong

两数之和 扩展版

2Sum 3Sum 4Sum 问题_第1张图片
2Sum 3Sum 4Sum 问题_第2张图片
nums = [1,3,1,2,2,3], target = 4
问题一:
[1,3] 和 [3,1] 就算重复,通过先排序再双指针可实现结果的按顺序排列。
问题二:
nums = [1,1,1,2,2,3,3], target = 4
[1,3] 肯定会重复
出问题的地方在于 sum == target 条件的 if 分支,当给 res 加入一次结果后,lo 和 hi 不应该改变 1 的同时,还应该跳过所有重复的元素:

    // 记录索引 lo 和 hi 最初对应的值
    int left = nums[lo], right = nums[hi];
    ...
    // 找到一组解后,跳过所有重复的元素
    while (lo < hi && nums[lo] == left) lo++;
    while (lo < hi && nums[hi] == right) hi--;    

时间复杂度是 O(N),而排序的时间复杂度是 O(NlogN),所以这个函数的时间复杂度是 O(NlogN)。

三数之和

15. 三数之和
现在想找和为 target 的三个数字,那么对于第一个数字,可能是什么?
nums 中的每一个元素 nums[i] 都有可能!
那么,确定了第一个数字之后,剩下的两个数字可以是什么呢?
其实就是和为 target - nums[i] 的两个数字呗,那不就是 twoSum 函数解决的问题么
需要把 twoSum 函数稍作修改即可复用。

不过的情况:
输入:[0,0],即数组长度小于3
在这里插入图片描述

// 已排序
function twoSum(le,ri,nums,target){
    let arr = []
    while(le<ri){
        let i = nums[le], j = nums[ri]
        if (i+j < target){
            while(le<ri && nums[le]==i) le++;
        } else if(i+j > target){
            while(le<ri && nums[ri]==j) ri--;
        } else{
            arr.push([i,j])
            while(le<ri && nums[le]==i) le++; // 去重
            while(le<ri && nums[ri]==j) ri--; // 去重
        }
    }
    return arr
}

var threeSum = function(nums) {
    let res = [], len = nums.length-1
    if(len < 2) return res;
    nums.sort((a, b) => a - b);
     for (let i = 0; i <= len ; i++){
         let tmp = twoSum(i+1,len,nums,0-nums[i])
         tmp.forEach(val => res.push([nums[i],...val]))
         while (i < len && nums[i] == nums[i + 1]) i++; // 去重
    }
    return res;
};

四数之和

2Sum 3Sum 4Sum 问题_第3张图片

不使用多个函数,参考三数之和,四数之和

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function(nums, target) {
    const length = nums.length;
    const res = [];
    nums.sort((a,b)=>a-b);
    if(length < 4) return res;
    for(let i=0; i<length-3; i++) {
        
        for(let j=i+1; j<length-2; j++) {
            let left = j+1, right = length-1;
            const presum = nums[i] + nums[j]
            while(left < right) {
                let le = nums[left], ri = nums[right];
                const sum = presum + le + ri
                if(sum == target) {
                    res.push([nums[i], nums[j], nums[left], nums[right]]);
                    while(left < right && nums[left]==le) left++; // 去重
                    while(left < right && nums[right]==ri) right--; // 去重
                }else if(sum > target){
                    while(left < right && nums[right]==ri) right--;
                }else{
                    while(left < right && nums[left]==le) left++;
                }
            }
            while (j < length-3 && nums[j] == nums[j + 1]) j++; // 去重
        }
        while (i < length-4 && nums[i] == nums[i + 1]) i++; // 去重
    }

    return res;
};

你可能感兴趣的:(前端从0加速,leetcode,算法,javascript)