三数之和 | 学习笔记

157 阅读2分钟

题目要求:

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1][-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

题目分析:

需要在给定的而数组中寻找符合要求的三数之和,且该元组中各元素的索引不同,元组不能相同。

解法:双指针解法

最左侧元素为指向遍历的各个元素,其次还有分别两个双指针:left和right

步骤:

  1. 那么我们在遍历之前,先要对数组进行一个排序。保证它是按顺序进行的排列

  2. 那么在我们可以像基础的双指针一样,先要保证左侧的指针要小于右侧的指针:while(left < right)

  3. 在满足条件的情况下需要判断三个元素是否相加为 0 。

  4. 若不满足则此时还有两种情况,一种是相加之后小于零,一种是相加之后大于零

    1. 相加小于零,由于我们数组是经过了大小的排序,所以小于零说明元素的值小了些。那么需要进行的操作就是让参与相加的元素变大,所以此时可以将左指针向右移动一位
    2. 相加大于零,说明元素的值大了些,那么需要让参与相加的元素变小,所以此时需要将右指针像左移动一位。
  5. 如果满足要求即相加为零,且索引不同,那么就将这三个元素push进数组进行存储。同时还要对这次操作进行处理

    1. 进行去重,也就是说数组可能是这样的:[-1,-1,-1,0,1,1,1,2,3]。那么你会发现,符合三数之和的元组有:[-1,-1,2]由于有三个一模一样的元素-1,虽然索引的元素不同,但是第0、1、2的元素都是-1都可以与2配合相加为零,但是不符合元组不同的要求。
    2. 先在遍历的时候就对重复的元素进行一个跳过,那么就能在外层对重复元素进行去重。
    3. 在内层进行第二个维度的去重,所以要判断该指针指向元素的下一个要进行的元素是否与当前的元素相等,如果相等,那么要在基础的递增(减)之上再次进行一次操作,跳过下一个重复的元素。

Talk is easy , show me code!

/*
*   @params { nums } 被操作数组
*/
const fun = (nums) => {
    // 排序数组 -> 双指针解法的准备工作
    nums.sort((a,b) => a-b);
    
    // 先进行判断,如果元素每个元素都是大于0那么就不会有三数相加等于0的情况了
    // 所以判断最小的元素是否是小于零的
    if(nums[0] > 0){
        return [];
    }
    
    // 存储结果集
    // 存储左右指针和被遍历的当前的元素
    let res = [];
    let left;
    let right;
    let curEle;
    
    // 遍历元素
    for(let i = 0; i < nums.length; i++){
        curEle = nums[i];
        left = i + 1;
        right = nums.length - 1;
        
        // 进行去重,去重的是最外层遍历的元素,
        // 去重有两个维度,一个是最外层遍历元素的维度吗,另一层是内部指针指向的维度
        // 内层遍历去重的是指针所指向下一个元素
        // 此处所针对的不一样,慢慢理解一下
        if(nums[i] === nums[i - 1]) {
            continue;
        }
        
        
        // 进行指针移动的操作
        while(left < right){
            // 左右指针所指向的元素,三数之和
            let leftEle = nums[left];
            let rightEle = nums[right]; 
            let threeSum = curEle + leftEle + rightEle;
            
            // 不满足条件那么根据步骤的描述,移动指针
            if(threeSum < 0) left++;
            else if(threeSum > 0) right--;
            else {
                // 说明符合条件,放进结果集
                // 并且进行去重
                res.push([curEle,leftEle,rightEle]);
                
                // 进行去重
                while(left < right && leftEle === nums[left + 1]){
                    left++;
                }
                while(left < right && rightEle === nums[right - 1]){
                    right--;
                }
                
                // 默认符合要求,移动指针。
                left++;
                right--;
            }
        }
    }
}

通过上面的双指针写法,就能够解出此题啦!

可以自己去试试吧

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/3sum
    

思路来源:代码随想录

总结:才才接触算是算法吧,本身对数据结构还是没咋接触的,就挑了其中最熟悉的数组吧。这两天关于双指针有些感觉了,但是接触新题还是没思路,这才是数组的第四题,还是有些难的啊啊啊。