前端算法(18)

54 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • v0 <= a, b, c, d < n
  • a、b、c 和 d 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

输入: nums = [1,0,-1,0,-2,2], target = 0
输出: [[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

解题思路

思路一

我们先进行排序,这样便于我们使用双指针的时候可以少一层循环,然后再将 前两个值需要用for循环遍历,后两个值可以直接使用双指针,在固定住第一个值,再固定第二个值,在第二个值后续的数组中,进行双指针遍历,我们这里需要注意去重,其去重思路也与三数之和一样,固定的前两个值,将其与其上一个值作比,相同则说明本次迭代结果如果存在解会与上次趋同,可跳过。双指针内部,则与三数之和一样,我们还需要注意提前退出的条件,如果已排序,所以在第一层循环固定了firstVal时,如果其 > (1/4)*target 说明本次迭代后续的整个结果集必然是都 > target 的,可以结束本层for的所有循环了,还有对于secondVal也是一样的,当firstVal + secondVal > (1/2)*target 说明结果集必然 > target,可以结束本层循环,回到第一层

var fourSum = function(nums, target) {
    const len = nums.length,
        res = [];
    if (len < 4) {
        return res;
    }
    nums = nums.sort((a, b) => a - b);
    for (let firstIndex = 0; firstIndex < len; firstIndex++) {
        const first = nums[firstIndex];
        // 第一个数就超过了目标值的 1/4,说明其与后面的数的组合必定 > target 可以退出循环了
        if (first > (target / 4)) {
            break;
        }
        // 去除重复值
        if (firstIndex > 0 && nums[firstIndex] === nums[firstIndex - 1]) {
            continue;
        }
        // 如当前循环值,与最末尾三数之和还是 < target,说明本次循环不可能找到 = target的情况,直接跳过
        if (nums[len - 1] + nums[len - 2] + nums[len - 3] + first < target) {
            continue;
        }
        for (let secondIndex = firstIndex + 1; secondIndex < len; secondIndex++) {
            const second = nums[secondIndex];
            // 去重,second与前一位比较(去除first是前一位的情况),如果相同则可跳过本次
            if ((secondIndex > firstIndex + 1) && (nums[secondIndex] === nums[secondIndex - 1])) {
                continue;
            }
            // 与上面循环同理,后二者数之和必然大于 first + second,故此必然超过target
            if (first + second > target / 2) {
                break;
            }
            // 与上面循环同理
            if (first + second + nums[len - 2] + nums[len - 1] < target) {
                continue;
            }
            let left = secondIndex + 1,
                right = len - 1;
            while (left < right) {
                const sum = first + second + nums[left] + nums[right];
                if (sum === target) {
                    res.push([first, second, nums[left], nums[right]]);
                    // 去重,当下一位left与当前left值相同,说明该组合结果已被记录过了,可直接跳过
                    while (nums[left] === nums[left + 1]) {
                        left++;
                    }
                    while (nums[right] === nums[right - 1]) {
                        right--;
                    }
                    // 当前组合已收集,将左右指针向中间移动
                    left++;
                    right--;
                } else if (sum < target) {
                    left++;
                } else {
                    right--;
                }
            }
        }
    }
    return res;
};