Leedcode 两数、三数、四数之和总结

简介: Leedcode 两数、三数、四数之和总结

1. 两数之和


难度简单


给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。


你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。


你可以按任意顺序返回答案。


22096a6f68d844399a47b456b66fc825.png

思路1:双重循环,时间复杂度O(n**2)

29e33e0aeb25438e9e78ea8eb018f164.png

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n = nums.size();
        for (int i = 0;i<n;i++)
        {
            for (int j = i+1;j<n;j++)
            {
                if (nums[i]+nums[j] == target) return {i,j};
            }
        }
        return {};
    }
};

思路2:unordered_map,因其查找复杂度可达到O(1),这道题的时间复杂度可达到O(n) ;


0b55bfddbcda478caf097462c7783e89.png

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> hash;
        int n = nums.size();
        for (int i = 0;i<n;i++)
        {
            if (hash.find(target-nums[i])!=hash.end()) return {i,hash[target-nums[i]]};
            else hash[nums[i]] = i;
        }
        return {};
    }
};

可见时间得到了优化的同时增加了空间的开销;


补充有关unordered_map的知识:


需要加入头文件


unordered_map的遍历,用迭代器,i->first,i->second,来访问key和val;


初始化不需要加" = ",创建新的key,val对和py的做法类似;


#include <iostream>
#include <unordered_map>
using namespace std;
int main()
{
    unordered_map<string,int>score{{"小明",98}};
    score["张三"] = 93;
    for (unordered_map<string,int>::iterator i = score.begin();i!=score.end();i++)
        cout<<i->first<<" "<<i->second<<endl;
}

15. 三数之和

难度中等

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组

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


9e8ecc55c1054683bb14fb9a8ad4376b.png

排序nlogn,双指针+循环 n**2,总复杂度 O(n**2)


思路:本题先排序,双指针才能起作用


**双指针算法最核心的用途就是优化时间复杂度,关注题目的单调性**


先定第一个数nums[i],那么就转化为在[i,n-1]区间内做两数之和的问题,令target = -nums[i];


如果用hash来做,在去重上会比较困难;


如果用双指针,迎刃而解;由于序列单调,两头和过大,r左移动,两头和过小,l右移动;


如果两头和(设为a,b)等于target,加入答案,然后l++,r--,如果nums[l] = nums[l-1],往后移动,直到不等于a,同理右边一直移动直到不等于b;


特别的,由于已经排过序,那么当nums[i](第一个数>0)的时候,必然无解,退出循环;


特别的,当nums[i] = nums[i-1],意味着对于第一个数是nums[i]的这轮寻找可以不执行。原因在于,如果存在合法解j,k,使得nums[i]+nums[j]+nums[k]=0,那么对于nums[i-1],j,k一定也是它的合法解,故一定会出现重复,所以就得到我们上述的结论,如果nums[i] = nums[i-1],continue;


543cb368306347728cba5a2bff25ed5b.png

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        int n = nums.size();
        sort(nums.begin(),nums.end());
        for (int i = 0;i<n;i++)
        {
            if (nums[i]>0)
                break;
            if (i>0&&nums[i] == nums[i-1])
                continue;
            int l = i+1, r = n-1,target = -nums[i];
            while (l<r)
            {
                if (target == nums[l]+nums[r])
                {   ans.push_back({nums[i],nums[l],nums[r]});
                    l++;
                    r--;
                    while (nums[l] == nums[l-1]&&l<r) l++;
                    while (nums[r] == nums[r+1]&&l<r) r--;
                }
                else if (target<nums[l]+nums[r]) r--;
                else l++;
            }
        }
        return ans;
    }
};

18. 四数之和


难度中等


给你一个由 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

你可以按 任意顺序 返回答案 。


ce32e94ad7ec4f4380261e087834afc4.png

思路:两层循环o(n**2)+排序nlogn+双指针n


第一层循环固定第一个数,转化为三数之和;


6536edbcbbe24344be514883289a3142.png


第二层循环固定第二个数,转化为两数之和;


重点在于去重:


道理和三数之和中的特别的,当nums[i] = nums[i-1],意味着对于第一个数是nums[i]的这轮寻找可以不执行。原因在于,如果存在合法解j,k,使得nums[i]+nums[j]+nums[k]=0,那么对于nums[i-1],j,k一定也是它的合法解,故一定会出现重复,所以就得到我们上述的结论,如果nums[i] = nums[i-1],continue;一样,如果nums[i] = num[i-1],那么会出现重复的四元解;


如果nums[j] = nums[j-1],那么会出现重复的三元解;


双指针内部的两层while,避免了重复的二元解;


!一些细节的地方,当我们判断nums[i] = nums[i-1]的时候,需要保证i>0;

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) 
    {
        vector<vector<int>> ans;
        int n = nums.size();
        if (n<4) return {};
        sort(nums.begin(),nums.end());
        for (int i = 0;i<n;i++)
        {   if (i>0&&nums[i] ==nums[i-1])//四数之和去重
                continue;
            int s3 = target - nums[i];
            for (int j = i+1;j<n;j++)
            {   if (j>i+1&&nums[j]==nums[j-1])//三数之和去重
                    continue;
                int s2 = s3 - nums[j];//两数之和
                int l = j+1, r = n-1;
                while (l<r)
                {
                    if (s2 == nums[l]+nums[r])
                    {   
                        ans.push_back({nums[i],nums[j],nums[l],nums[r]});
                        l++;
                        r--;
                        while (nums[l] == nums[l-1]&&l<r) l++;
                        while (nums[r] == nums[r+1]&&l<r) r--;//两数之和去重
                    }
                    else if (s2<nums[l]+nums[r]) r--;
                    else l++;
                }
            }
        }
        return ans;
    }
};
相关文章
|
2月前
【LeetCode 14】454.四数相加 II
【LeetCode 14】454.四数相加 II
35 1
|
2月前
【LeetCode 16】15.三数之和(双指针法)
【LeetCode 16】15.三数之和(双指针法)
39 1
|
7月前
|
索引 容器
双指针解决leetcode1两数之和
双指针解决leetcode1两数之和
50 0
|
22天前
两数之和
给定整数数组 `nums` 和目标值 `target`,任务是在数组中找到和为 `target` 的两个整数并返回它们的下标。每个输入保证有唯一解,且不能重复使用同一元素。示例展示了不同情况下的输入与输出,暴力破解法通过两层循环遍历所有可能的组合来寻找解。
|
6月前
18.四数之和
18.四数之和
|
7月前
15. 三数之和
15. 三数之和
46 3
|
7月前
18. 四数之和
18. 四数之和
46 2
|
6月前
15.三数之和
15.三数之和
|
7月前
|
人工智能 Java C++
leetcode-454:四数相加 II
leetcode-454:四数相加 II
48 1
|
算法
【算法专题突破】双指针 - 四数之和(8)
【算法专题突破】双指针 - 四数之和(8)
37 0