【LeetCode & 剑指offer刷题】数组题3:3Sum(系列) + 4sum

【LeetCode & 剑指offer 刷题笔记】目录(持续更新中...)

3Sum

Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
The solution set must not contain duplicate triplets.
Example:
Given array nums = [-1, 0, 1, 2, -1, -4],
 
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]

C++
 
//问题:找数组中和为0的三元组
//方法:排序后,扫描数组,转化为2sum问题
//2sum对应一个循环,3sum对应两重循环,4sum对应三重循环
//O(n^2)
#include <algorithm>
class Solution
{
public:
    vector<vector<int>> threeSum(vector<int>& a)
    {
        vector<vector<int>> res;
        int n = a.size();
        if(n<3) return res;
       
        sort(a.begin(), a.end()); //排序
 
        //扫描a[i],后面在用leftright首尾两指针扫描
        for(int i = 0; i<n && a[i]<=0; i++) //第一个数只能是负数或0,加此判断以节省时间,如果不加次判断,则i<n-2即可
        {
            int target = -a[i]; //将第一个数的相反数定为2sum的target 
            int left = i+1//i=0~n-3
            int right = n-1;
 
            while(left < right) //用两个指针分别从a[i+1]和整个数组末尾开始向中间扫描 ,找到所有可以满足和为-a[i]的数对
            {
                int sum = a[left] + a[right];
                if(sum < target) left++; //仅移动前面指针
                else if(sum > target) right--; //仅移动后面指针
                else //满足3sum要求
                {
                    res.push_back({a[i], a[left], a[right]});  //将满足的三元组push到结果向量中(也可以用vector<int>{a[i], a[left], a[right]})
 
                    while(left<right && a[left+1] == a[left]) left++; //以免第二个数重复
                    while(left<right && a[right-1] == a[right]) right--;//以免第三个数重复
                    left++; //前后指针都移动,下一次判断
                    right--;
                }
            }
            while(i+1 < n && a[i+1] == a[i]) i++; //以免第一个数重复
        }
       
        return res;
    }
};
/*注:
也可用set避免重复
set<vector<int>> res;
...
vector<vector<int>>(res.begin(), res.end());//用迭代器将set转为vector
*/
 
16. 3Sum Closest
Given an array nums of n integers and an integer target, find three integers in nums such that the sum is closest to target. Return the sum of the three integers. You may assume that each input would have exactly one solution.
Example:
Given array nums = [-1, 2, 1, -4], and target = 1.
 
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

 
/*
问题:离目标值最近的三数之和
方法:排序后,扫描a[i],后面在用leftright首尾两指针扫描
*/
class Solution
{
public:
    int threeSumClosest(vector<int>& nums, int target)
    {
        if(nums.size()<3) return 0;
       
        int closest = nums[0] + nums[1] + nums[2];
        int diff = abs(closest - target);
        sort(nums.begin(), nums.end()); //排序
       
        //扫描a[i],后面在用leftright首尾两指针扫描
        for (int i = 0; i < nums.size() - 2; i++)  //i=0~n-3(n-2,n-1分别为leftright占着)
        {
            int left = i + 1, right = nums.size() - 1; // left = i+1, right=n-1
           
            while (left < right)
            {
                int sum = nums[i] + nums[left] + nums[right];
                int newDiff = abs(sum - target);
                if (newDiff < diff ) //更新diffsum
                {
                    diff = newDiff;
                    closest = sum;
                }
               
                if (sum < target) left++; //调节指针
                else right--;
            }
        }
        return closest;
    }
};
 
18. 4Sum
Given an array nums of n integers and an integer target, are there elements a, b, c, and d in nums such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
Note:
The solution set must not contain duplicate quadruplets.
Example:
Given array nums = [1, 0, -1, 0, -2, 2], and target = 0.
 
A solution set is:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]

Seen this question in a real interview before?  YesNo
 
 
/*
问题:找与目标值相等的4个数
三重循环即可
set可避免重复结果
*/
class Solution
{
public:
    vector<vector<int>> fourSum(vector<int> &nums, int target)
    {
        if(nums.size() < 4) return vector<vector<int>>(); //或者用{{}}
       
        set<vector<int>> res;
        sort(nums.begin(), nums.end());
       
        //扫描a[i],a[j]后面接left,right两个指针
        for (int i = 0; i < int(nums.size() - 3); i++)
        {
            for (int j = i + 1; j < int(nums.size() - 2); j++)
            {
    //            if (j > i + 1 && nums[j] == nums[j - 1]) continue; //遇到重复数时不执行下面语句,如果用set可以不进行此判断
               
                int left = j + 1, right = nums.size() - 1//i=0~n-4,j=i+1~n-3
                while (left < right)
                {
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum == target)
                    {
                        vector<int> out{nums[i], nums[j], nums[left], nums[right]};
                        res.insert(out); //set,当有重复结果时,插入会失败
                        left++; right--;
                    }
                    else if (sum < target) left++;
                    else right--;
                }
            }
        }
        return vector<vector<int>>(res.begin(), res.end()); //set转化为vector输出
    }
};
 

 

posted @ 2019-01-05 13:46  wikiwen  阅读(220)  评论(0编辑  收藏  举报