头图

前言:
在之前做题中经常被去重问题困扰,其中最常见的去重思想就是使用集合本身的特性进行去重,比如使用set集合或者map集合进行去重,防止相同元素的重复加入,但是在有些问题时,需要去重的不仅仅是元素,可能是一整个数组或者其他集合,这个时候如果进行一一比较,可能就比较繁琐,需要通过代码逻辑来实现避免重复对象的插入。这里有道题可以很好的理解去重的逻辑,给大家展示一下。

leetcode之三数之和

注意:

本题需要注意的就是去重的逻辑。了解不包含重复元素和不包含重复数组的区别。而且去重是保证已经收集了满足条件的结果以后在进行去重操作。不可以先去重。本题是不包含重复数组,所以在遇到第一个重复元素的时候要先采集所有结果,在后边遇到和他相同元素的时候在进行去重。不可以在第一次遇到的时候就进行去重操作。

1、题目要求:
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]

2、题目难点
使用双指针来找到目标值。主要是要了解去重的操作。了解不包含重复三元组和三元组内不包含重复元素的区别。

3、解题思路:

首先是定义一个i放在起始位置,然后定义left为i+1,right定义在末尾。
外层循环是遍历i的位置。内层循环是利用双指针来寻找nums[left] + nums[right] == -nums[i]的情况,然后进行收集结果。
我们在刚开始的时候会对数组进行初始化处理,对数组进行从小到大排序。所以当i固定,nums[i] + nums[right] + nums[left] < 0的时候,我们需要将left向右移动。因为left向右移动以后,nums[left]数值会变大,结果才会变大。相反的话,就需要将right向左移动,将整个数组的结果变小。
去重的操作:
去重首先就是要保证对于满足条件的数组,先收集到结果以后再进行去重操作。不可以提前去重了。本题需要考虑三个数的去重操作。 对于i的去重操作。是判断 nums[i] 与 nums[i + 1]是否相同,还是判断 nums[i] 与 nums[i-1] 是否相同。
遇到满足条件的,先收集结果在去重。区分数组中不包含重复元素和结果中不包含重复数组。
如果采用第一种方式的话,例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断下一个也是-1,那这组数据就pass了。所以我们应该采取的操作时,如果遇到第一个-1时,继续进行判断后面所有满足条件的,然后等第一个-1所有得到的结果采集完毕以后,再进行去重操作。
如果在遇到第一个-1时,直接指针向后移动来进行去重的话,那么题目应该改为在数组中不包含相同元素(这种情况下,两个-1一定不会同时取到)。
`我们要做的是 不能有重复的三元组,但三元组内的元素是可以重复的!
所以我们选择使用第二种情况,代码如下:`
c++ 代码解读复制代码if (i > 0 && nums[i] == nums[i - 1]) {

      continue;

}

当处理完第一个-1以后,遍历到第二个-1的时候,就进行比较。如果和之前元素的相同,那么就直接跳过。
 
对于left和right的去重操作:
当找到nums[left] + nums[right] + nums[i] == 0 的情况时,也是先采集结果,将三个数放入到数组中,然后对left和right下一个遍历的数进行判断。如果相同的话,则直接进行去重(指针跳过该数)。
在找到满足结果的数组时,一定是先收集结果,在进行去重。防止漏掉结果。
4、代码:
c++ 代码解读复制代码class Solution {

public:

    vector<vector<int>> threeSum(vector<int>& nums) {

        vector<vector<int>> result;

        sort(nums.begin(), nums.end());

        // 找出a + b + c = 0

        // a = nums[i], b = nums[left], c = nums[right]

        for (int i = 0; i < nums.size(); i++) {

            // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了

            if (nums[i] > 0) {

                return result;

            }

            // 错误去重a方法,将会漏掉-1,-1,2 这种情况

            /*

            if (nums[i] == nums[i + 1]) {

                continue;

            }

            */

            // 正确去重a方法

            if (i > 0 && nums[i] == nums[i - 1]) {

                continue;

            }

            int left = i + 1;

            int right = nums.size() - 1;

            while (right > left) {

                // 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组

                /*

                while (right > left && nums[right] == nums[right - 1]) right--;

                while (right > left && nums[left] == nums[left + 1]) left++;

                */

                if (nums[i] + nums[left] + nums[right] > 0) right--;

                else if (nums[i] + nums[left] + nums[right] < 0) left++;

                else {

                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});

                    // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重

                    while (right > left && nums[right] == nums[right - 1]) right--;

                    while (right > left && nums[left] == nums[left + 1]) left++;

 

                    // 找到答案时,双指针同时收缩

                    right--;

                    left++;

                }

            }

 

        }

        return result;

    }

};


幸运的行者
1 声望0 粉丝