Bootstrap

优选算法:三数目标之和(双指针)

        题目详情:. - 力扣(LeetCode) 

一. 题目解析

        题目要求还是比较好理解的:给出一个数组,要求找出三个元素相加等于0的所有情况,并且不能重复。这里的不能重复指的是三元组里面的元素不能完全相同,与顺序无关只要元素不完全相同即可

二. 算法分析

        这个处理步骤与我上一篇文章中使用的方法有类似的,大家可以先去看看有助于更好的理解这里的方法(优选算法:两数目标和(双指针)-CSDN博客)。

        话回正题。首先,我们还是先使用sort函数对整个数组进行排序。然后这里是求三个元素相加的和,如果三个动态的我们是不太好考虑的,所有我们可以先固定一个数,然后只考虑两个动态的数,其实就相当于:两个数的目标和了,那么这样的话就很好考虑了。

         我们先考虑如何找全所有情况,在考虑去重的问题。

        一. 找全所有情况

        由于要求返回多个三元组所以我们创建一个二维数组 ret 用于存储结果。如上图所示,我们定义出两个指针 left right,利用循环判断 fix+left+right值与0的关系。如果刚好等于0直接压入数组 ret,然后left++,right-- 。如果大于0,则right-- 再次进入循环判断。如果小于0,则left-- 再次进入循环判断。直到left 与 right相遇时,fix位置的情况考虑完毕,fix++,进入新的大循环。

        二. 考虑去重情况

        

         如图(红色箭头)此时满足条件压入ret后,left++ right--,我们发现left指向的仍是1 right指向的仍是3,这种情况我们是已经计算过了的不能再次压入栈,所以我们可以借助while循环跳过重复的元素:while(left<right&&nums[left]==nums[left-1])left++;   while(left<right&&nums[right]==nums[right+1])right--; 一直让left right指向元素2 。

        考虑完left和right下面我们考虑考虑fix的情况(如图紫色箭头)。当left right相遇时fix++进入新的大循环,但是我们回返现fix可能会出现与之前重复的情况。一旦fix出现重复那么就意味着上一次大循环的结果会再次的被我们计算一次。所以这里我们也需要借助while循环跳过重复元素 :while(fix>0&&fix<=nums.size()-3&&nums[fix]==nums[fix-1])fix++; 这里fix需要小于等于nums.size()-3 是因为fix作为最左边的元素要保证剩余的空间是足够容纳下三个元素的

三. 代码实现

class Solution {
public:
    vector<vector<int>> path;
    vector<vector<int>> threeSum(vector<int>& nums)
    {
        sort(nums.begin(), nums.end());//排序
        int fix = 0;
        for (; fix <= nums.size() - 3; fix++)
        {
            while (fix&&fix<=nums.size()-3 && nums[fix] == nums[fix - 1])//fix跳过重复元素
                fix++;

            int left = fix + 1, right = nums.size() - 1;
            while (left < right)
            {
                int sum = nums[fix] + nums[left] + nums[right];
                if (sum == 0)
                {
                    path.push_back({ nums[fix],nums[left],nums[right] });
                    left++;
                    right--;

                    //left和right跳过重复元素
                    while (left<right&&nums[left] == nums[left - 1]) left++;
                    while (left<right&&nums[right] == nums[right+1]) right--;
                }
                else if (sum < 0)
                    left++;
                else
                    right--;
            }
        }
        return path;
    }
};

 

;