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;
    }
};
相关文章
|
云安全 安全 小程序
等保测评|全面理解渗透测试
在数字化转型的大潮中,企业和组织纷纷拥抱互联网以拓展市场和服务客户,这不仅促进了业务发展,也带来了网络安全的新挑战。为了保护在线的机密文件和知识产权不受黑客攻击,渗透测试成为一种关键的安全评估手段。它通过模拟攻击来查找系统漏洞,帮助企业提前修补安全缺口。本文将介绍渗透测试的概念、必要性及主要执行方式,并探讨如何选择合适的测试服务机构,以确保企业的数字资产安全无虞。
|
Java 编译器 C++
【Java基础面试一】、为什么Java代码可以实现一次编写、到处运行?
这篇文章解释了Java能够实现“一次编写,到处运行”的原因,主要归功于Java虚拟机(JVM),它能够在不同平台上将Java源代码编译成的字节码转换成对应平台的机器码,实现跨平台运行。
【Java基础面试一】、为什么Java代码可以实现一次编写、到处运行?
|
Linux 开发工具 数据安全/隐私保护
CentOS7开机提示welcome to emergency mode!after logging in...
CentOS7.3昨天用的还好好的的,但是今天开机提示如下(如图提示): welcome to emergency mode!after logging in ,type “journalctl -xb” to view system logs,“systemctl reboot” to reboot ,“systemctl default” to try again to boot into default mode。
5327 0
|
域名解析 Linux 网络安全
Apache配置虚拟主机----基于域名的虚拟主机技术
Apache配置虚拟主机----基于域名的虚拟主机技术
593 0
|
12月前
|
SQL 数据采集 人工智能
“服务器老被黑?那是你没上AI哨兵!”——聊聊基于AI的网络攻击检测那些事儿
“服务器老被黑?那是你没上AI哨兵!”——聊聊基于AI的网络攻击检测那些事儿
471 12
|
Swift
DeepSeek开源Janus-Pro多模态理解生成模型,魔搭社区推理、微调最佳实践
Janus-Pro是DeepSeek最新开源的多模态模型,是一种新颖的自回归框架,统一了多模态理解和生成。
1183 19
DeepSeek开源Janus-Pro多模态理解生成模型,魔搭社区推理、微调最佳实践
|
存储 JavaScript 前端开发
|
12月前
|
存储 监控 数据中心
Microsoft System Center 2025 version 2503 Multilanguage - Windows 服务器管理软件
Microsoft System Center 2025 version 2503 Multilanguage - Windows 服务器管理软件
404 0
|
人工智能 缓存 Ubuntu
AI+树莓派=阿里P8技术专家。模拟面试、学技术真的太香了 | 手把手教学
本课程由阿里P8技术专家分享,介绍如何使用树莓派和阿里云服务构建AI面试助手。通过模拟面试场景,讲解了Java中`==`与`equals`的区别,并演示了从硬件搭建、语音识别、AI Agent配置到代码实现的完整流程。项目利用树莓派作为核心,结合阿里云的实时语音识别、AI Agent和文字转语音服务,实现了一个能够回答面试问题的智能玩偶。课程展示了AI应用的简易构建过程,适合初学者学习和实践。
566 22
|
存储 缓存 算法
RAID 的镜像是一种冗余技术
镜像是冗余技术的一种,通过在不同磁盘上创建数据的完整副本,提供数据保护。这种方法无需额外计算和校验,故障恢复迅速,支持并发读取,提高读I/O性能,但写入性能受影响。镜像技术虽提供高数据安全性,却需双倍存储空间,成本较高,适用于关键数据保护。此外,镜像可通过“拆分”实现几乎零备份窗口的数据备份。
534 4