两数之和 三数之和 四数之和

本文详细解析了LeetCode中的1两数之和、15三数之和和18四数之和的解决方案。主要方法包括双指针法和哈希表存储,重点介绍了如何通过迭代和去重技巧有效地找到满足条件的数字组合。文章指出,对于三数之和问题,双指针法更为适用。
摘要由CSDN通过智能技术生成

一、 LeetCode 1 两数之和

LeetCode 1.两数之和

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> umap;//value weizhi
        //unordered_map是一种key value的存储结构
        //可以用key保存数值,用value在保存数值所在的下标。
        for(int i=0;i<nums.size();++i)
        {
            umap[nums[i]]=i;//初始化
        }

        for(int i=0;i<nums.size();++i)
        {
            auto it=umap.find(target-nums[i]);
            if(it!=umap.end()&&it->second!=i)
            {
                return {i,it->second};
            } 
        }
        return {};
    }
};

哈希表存储。

二、LeetCode 15 三数之和

LeetCode 15.三数之和

1. 双指针法

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());
        for(int i=0;i<nums.size();++i)
        {
            if(nums[i]>0) break;
            if(i>0&&nums[i]==nums[i-1]) continue;//三元组元素a去重
            
            //双指针 
            int low=i+1,high=nums.size()-1;
            while(low<high)
            {
                if(nums[low]+nums[high]+nums[i]>0)
                {
                    high--;
                    while(low<high&&nums[high]==nums[high+1]) high--;//去重1
                }
                else if(nums[low]+nums[high]+nums[i]<0) 
                {
                    low++;
                    while(low<high&&nums[low]==nums[low-1]) low++;//去重2
                }
                else if(nums[low]+nums[high]+nums[i]==0) 
                {
                    result.push_back({nums[low],nums[high],nums[i]});
                    // 去重逻辑应该放在找到一个三元组之后
                    while(low<high&&nums[high]==nums[high-1]) high--;//去重3
                    while(low<high&&nums[low]==nums[low+1]) low++;//去重4
                    // 找到答案时,双指针同时收缩
                    low++; high--;
                }
            }
        }
        return result;
    }
};

去重的原理都是没有比较过的元素与相邻的元素之间进行的比较。

去重1说明:

去重1中,定义high的位置为pos1,首先进行的是high–,此时high的值为pos2=pos1-1。

因为pos1之前没有进行过比较,因此需要将其与相邻的元素进行比较,即pos2=pos1-1,所以应该比较nums[pos1]和nums[pos1-1]。

由于此时的high=pos1-1,因此,需要比较nums[high]==nums[high+1]。

if(nums[low]+nums[high]+nums[i]>0)
{
   high--;
   while(low<high&&nums[high]==nums[high+1]) high--;//去重1
}

去重2同去重1理。

去重3说明:

去重3中,定义high的位置为pos1,因为pos1之前没有进行过比较,因此需要将其与相邻的元素进行比较,即pos2=pos1-1,所以应该比较nums[pos1]和nums[pos1-1]。由于此时的high=pos1,因此,需要比较nums[high]==nums[high-1]。

while(low<high&&nums[high]==nums[high-1]) high--;//去重3

去重4同去重3理。

2. 哈希解法

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());//-4 -1 -1 0 1 2
        for(int i=0;i<nums.size();++i)
        {
            if(nums[i]>0) break;
            if(i>0&&nums[i]==nums[i-1]) continue;//三元组元素a去重
            
            unordered_set<int> set;
            for(int j=i+1;j<nums.size();++j)
            {
                if(j>i+2&&nums[j]==nums[j-1]&&nums[j-1]==nums[j-2]) continue;// 三元组元素b去重

                int tmp=0-(nums[i]+nums[j]);
                if(set.find(tmp)!=set.end())
                {
                    result.push_back({nums[i],nums[j],tmp});
                    set.erase(tmp);// 三元组元素c去重
                }
                else
                {
                    set.insert(nums[j]);
                }
            }
        }
        return result;
    }
};

哈希解法中需要注意的是去重。

对于a的去重用if语句:

if(i>0&&nums[i]==nums[i-1]) continue;//三元组元素a去重

对于b的去重,则需要进行如下的判断:

if(j>i+2&&nums[j]==nums[j-1]&&nums[j-1]==nums[j-2]) continue;// 三元组元素b去重

对于c的去重,需要如下的操作:

set.erase(tmp);// 三元组元素c去重

结论:方法1(双指针法)更加适合三数之和,判断去重更加的方便。

三、LeetCode 18 四数之和

LeetCode 18.四数之和

基本框架同三数之和的双指针法。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());

        for(int i=0;i<nums.size();++i)
        {
            if(i>0&&nums[i]==nums[i-1]) continue;
            for(int j=i+1;j<nums.size();++j)
            {
                if(j>i+1&&nums[j]==nums[j-1]) continue;

                int low=j+1;
                int high=nums.size()-1;

                while(low<high)
                {
                    if(nums[i]+nums[j]>target-(nums[low]+nums[high]))
                    {
                        high--;
                        while(low<high&&nums[high]==nums[high+1]) high--;
                    }
                    else if(nums[i]+nums[j]<target-(nums[low]+nums[high]))
                    {
                        low++;
                        while(low<high&&nums[low]==nums[low-1]) low++;
                    }
                    else if(nums[i]+nums[j]==target-(nums[low]+nums[high]))
                    {
                        result.push_back({nums[i],nums[j],nums[low],nums[high]});
                        while(low<high&&nums[high]==nums[high-1]) high--;
                        while(low<high&&nums[low]==nums[low+1]) low++;
                        low++;
                        high--;
                    }
                }
            }
        }
        return result;
    }
};

需要注意的是需要写成

if(nums[i]+nums[j]>target-(nums[low]+nums[high]))

而不是

if(nums[i]+nums[j]+nums[low]+nums[high]>target)

会报错,如下:

执行出错信息:
Line 19: Char 39: runtime error: signed integer overflow: 2000000000 + 1000000000 cannot be represented in type 'int' (solution.cpp)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior prog_joined.cpp:28:39
最后执行的输入:
[1000000000,1000000000,1000000000,1000000000]
0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值