跳至内容

L1 两数之和

题目链接

https://leetcode.cn/problems/two-sum/description/

题目描述

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

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

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

示例

示例 1

输入: nums = [2,7,11,15], target = 9

输出: [0,1]

解释: 因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2

输入: nums = [3,2,4], target = 6

输出: [1,2]

示例 3

输入: nums = [3,3], target = 6

输出: [0,1]

提示

  • 2nums.length1042 \le \text{nums.length} \le 10^4
  • 109nums[i]109-10^9 \le \text{nums}[i] \le 10^9
  • 109target109-10^9 \le \text{target} \le 10^9
  • 只会存在一个有效答案

题解

题目理解起来不难:遍历每个数,记为 now,然后看 target - now 有没有出现过,出现的话就找到结果了,直接返回即可。

最简单直接的办法就是双重循环暴力求解,但是看题目数据范围,这样大概率会超时,因此需要在此基础上找到一个更优的解法。

C++ 的 unordered_map 很适合这种情况,它是 C++11 之后提供的哈希表容器,无序存储、键唯一、查找平均 O(1),其常见用法如下表:

功能示例代码备注
创建unordered_map<int,string> mp;空哈希表
添加 / 修改mp[1] = "张三";存在就覆盖,不存在新建
安全查找 key 是否存在mp.count(1)返回 0 不存在 / 1 存在
读取数值mp.at(1)找不到直接报错,不会自动新建
迭代器查找auto it = mp.find(1)it != mp.end() 代表找到
删除mp.erase(1)按键删除
清空mp.clear()-
元素个数mp.size()-

借助 unordered_map,代码实现如下:

L1 - 两数之和 C++
#include <bits/stdc++.h>

using namespace std;

class Solution 
{
    public:
        vector<int> twoSum(vector<int>& nums, int target) 
        {
            unordered_map<int,int> mp; // 创建哈希表

            for(int i = 0; i < nums.size(); i++) 
            {
                int now = nums[i]; // 当前值
                int goal = target - now; // 当前值对应的目标值,也就是要找的值

                // 注意:在这里为了方便查找使用,把数组中的元素当做键,把数组下标当做值
                if(mp.count(goal)) // 找到键为 goal 的记录
                {
                    return {mp.at(goal),i};
                }

                else // 没有找到键为 goal 的记录,需要把当前元素放入
                {
                    mp[now] = i; // 记录键为当前元素,值为数组下标
                    // 注意:如果键已存在,会覆盖旧值。本题中由于找到答案即返回,不会出现重复键的情况。
                }
            }
            return {}; // 理论上说是不会到这一步的,因为题目规定答案存在,这里返回是语法要求
        }
};

// 注意:以下为自行补充的测试示例
int main()
{
    Solution test;

    vector<int> nums = {2,7,11,15};
    int target = 9;
    vector<int> res = test.twoSum(nums, target);
    
    // 输出结果
    for(int x : res) // 依次取出 res 里面的每一个整数,赋值给变量 x,循环执行
    {
        cout << x << " ";
    }
    cout << endl;
    return 0;
}

力扣平台只需提交 class Solution,可以在线编写并且进行评测,但如果想要使用自己的开发工具进行编写,就需要补充完整的测评代码了。上面的示例代码中含有 main(),之后的代码也可以仿照这种样式自行构造输入输出,写死测试示例或者实时读入测试数据都是可以的。后续的博客中只会放置 class Solution 部分的代码,完整的代码会整理至个人 GitHub 仓库:C571467648/blog,如有需要可以自行下载,别忘了顺手 star 一下哦~

言归正传,现在简单讲解一下代码:

我们用变量 int now = nums[i] 表示当前正在处理的数组元素,用 int goal = target - now 表示当前元素所需要配对的"目标元素",后续的任务就变成了这个目标元素 goal 是否出现过。如果 goal 在之前出现过,那么就找到了答案直接返回即可,否则把当前的元素记录到哈希表中,等待后续被查找。

特别需要注意的是,为了方便查找使用,把数组中的元素当做键,把数组下标当做值,千万不能弄混了!

mp.count(goal) 配合 mp.at(goal) 需要两次独立的查找操作。而 mp.find(goal) 一次查找就能同时得知存在性和值,代码更简洁、效率也略高一些。不过在实际运行中,这种差异通常很小,使用哪种写法都可以。(关于迭代器这部分可以参考 C++ 专栏的 STL,如果还没有更新,也可以先自行搜索学习,这里不过多讲解)

L1 - 两数之和 C++
class Solution 
{
    public:
        vector<int> twoSum(vector<int>& nums, int target) 
        {
            unordered_map<int,int> mp; 

            for(int i = 0; i < nums.size(); i++) 
            {
                int now = nums[i]; 
                int goal = target - now;
                auto it = mp.find(goal);

                if(it != mp.end())
                    return {it->second,i};
                else
                    mp[now] = i; 
            }
            return {};
        }
};

这里通过 find(goal) 直接获取指向 goal 的迭代器(如果没有找到,会指向 mp.end()),然后通过 it->second 直接获取值。(扩展一下:it->first 可以获取键,即数组元素的值)

运行结果

本题讲解就到这里啦,欢迎交流讨论~

交流与指正

本文内容主要来自个人学习与实践总结,受限于个人技术水平,难免存在理解不准确或表述疏漏等错误。 若您发现问题,或愿意就相关内容进一步交流,欢迎通过邮箱 571467648@qq.com 与我联系。感谢您的阅读与指正。