【力扣刷题记 18】——《四数之和》

Offer 驾到,掘友接招!我正在参与 2022 春招打卡活动,点击查看活动详情

一、题目描述:

  1. 四数之和-难度中等

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

0 <= a, b, c, d < n a、b、c 和 d 互不相同 nums[a] + nums[b] + nums[c] + nums[d] == target 你可以按 任意顺序 返回答案 。

示例 1:

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

输入:nums = [2,2,2,2,2], target = 8 输出:[[2,2,2,2]]  

提示:

1 <= nums.length <= 200

-10^9 <= nums[i] <= 10^9

-10^9 <= target <= 10^9

二、题目和思路分析:

返回等于目标值的数组,其实和三数之和差不多,这么想起来,循环加双指针即可。

但实际写起来才发现,和三数还是有区别的,三数的时候,以for循环当前下标为i的数字为第一个数,然后下标为i之后的数组,最前一个值下标为left和最后一个值下标为right,加起来一共三个值,结果大于目标值就减最后一个值的下标,结果小于目标值就加最前一个值的下标。保证了每个组合都会被考虑进去。

那么四数之和的第四个数从哪来呢?

我原本想着第四个数是第一个数的下一个数,即下标为i+1的数字,但实际写的时候发现第二个数字并不应该永远挨着第一个数字,比如[1,2,3,4,5,6,7],如果想要的结果是[1,3,5,7],那么按下标i,i+1,left,right去取,永远不会有正确答案,所以四个数字可能存在的位置应该没有任何的关系。

那么该怎么考虑呢?

我们可以把第四个数字下标以j表示,j取值范围为 left 和 right 之间,即left < j < right。

逻辑想好之后,剩下的就是写了好几遍才通过的代码了。

三、代码:

代码实现如下:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function(nums, target) {
    const length = nums.length
    if(nums == null || length < 4){
        return []
    }
    let arr = [] // 声明空数组
    nums.sort((a, b) => a - b) // 数组从小到大进行排序
    // console.log(nums)
    for (let i = 0; i < length ; i++) { // 确定第一个数字,初始值为nums[0]
        if(nums[i] > target && !(nums[i]<0 && target<0)){ // 如果最小数字大于target,那么和也一定大于target, 负数应该除外
            break
        }
        if(i > 0 && nums[i] == nums[i-1]){ // 当前数字与上个数字相同,说明已经循环过了,跳出本次循环
            continue
        }
        let left = i+1  // 确定第二个数字,初始值为nums[1]
        let right = length-1 // 确定第四个数字,初始值为最后一个数字
        while(left < right-1){
            let j = left+1 // 确定第三个数字,初始值为第二个数字和第四个数字中间的第一个数字
            while(j<right){
                // console.log(i, left, j, right)
                const sum = nums[i] + nums[left] + nums[j] + nums[right]
                // console.log(sum, target)
                if(sum == target){
                    // console.log(i, left, j, right)
                    arr.push([nums[i], nums[left], nums[j], nums[right]])
                    j++
                    while(j<right && nums[j] == nums[j-1]){
                        // console.log('跳过',j)
                        j++ 
                    }
                }else if (sum > target){
                    right--
                // }else if (sum < target){
                //     j++
                }else{
                    j++
                }
            }
            while (left<right && nums[left] == nums[left+1]){
                left++  
            }
            left++
            right = length-1
        }
    }        
    return arr
};

复制代码

四、总结:

本来我想,解题逻辑已经没问题了,代码写起来应该很快。但是写代码时总是考虑不周到,就像逻辑只是一个大纲,一条路,而写代码这个实现的过程并不比想逻辑要简单,也是充满了坎坷陷阱。

加油吧!

image.png

猜你喜欢

转载自juejin.im/post/7079352167458406413