← 返回文章列表

1-两数之和

题目:

给定一个整数数组 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]

提示:

  • 2 <= nums.length <= 10<sup>4</sup>
  • -10<sup>9</sup> <= nums[i] <= 10<sup>9</sup>
  • -10<sup>9</sup> <= target <= 10<sup>9</sup>
  • 只会存在一个有效答案

进阶: 你可以想出一个时间复杂度小于 O(n<sup>2</sup>) 的算法吗?


解法一:直接穷举

代码如下:

for(int i=0;i<nums.size();i++)
        {
            for(int j=i+1;j<nums.size();j++)
            {
                if(nums[i]+nums[j]==target)
                {
                    return {i,j};
                }
            }
        }
        return {};

将任意两数之和一个一个算出来,直到出现目标值


解法二:哈希算法

代码如下:

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

代码解析:

unordered_map<int, int> hashtable;
//用unordered_map函数创建一个哈希表,命名为hashtable,前一个int放入key(键),后一个int放入value(值)。
//在这段代码里:key(键)放的是数组里的数值nums[i],value(值)放的是这个数值对应的下标i。

for (int i = 0; i < nums.size(); ++i) {
//用for循环从数组第0个元素开始,一个一个往后遍历。i是当前元素的下标,nums[i]是当前这个数,循环到数组末尾为止。

    auto it = hashtable.find(target - nums[i]);
    //先算 target - nums[i],得到当前这个数还差多少才能凑成target,这个差值就是当前数的"搭档"。
    //再用find去哈希表里查这个搭档有没有出现过;找到了就返回指向那条记录的迭代器,没找到就返回hashtable.end()。
    //find的查询结果存进变量it,auto让编译器自动推断it的类型,不用手写长长的迭代器类型。

    if (it != hashtable.end()) {
    //判断it是否不等于hashtable.end()。不等于就说明找到了搭档;等于就说明没找到。

        return {it->second, i};
        //找到搭档了,返回两个下标:it->second是搭档的下标(哈希表里那条记录的值),i是当前数的下标。这两个下标对应的数加起来正好是target。

    }

    hashtable[nums[i]] = i;
    //没找到搭档,就把当前这个数存进哈希表:key(键)放数值nums[i],value(值)放下标i。
    //注意这一句在find之后,保证哈希表里只有之前处理过的数,没有当前数自己,所以不会出现自己配自己的情况。

}
//for循环结束。

return {};
//如果整个数组遍历完都没返回过,说明找不到任何一对数加起来等于target,返回一个空结果。

解法三:排序 + 双指针

代码如下:

int n = nums.size();
vector<pair<int, int>> a(n);
for (int i = 0; i < n; ++i) a[i] = {nums[i], i};
sort(a.begin(), a.end());

int l = 0, r = n - 1;
while (l < r) {
    int s = a[l].first + a[r].first;
    if (s == target) return {a[l].second, a[r].second};
    if (s < target) ++l;
    else            --r;
}
return {};

代码解析:

int n = nums.size();
//把数组长度存进n。

vector<pair<int, int>> a(n);
//创建一个长度为n的数组a,a里每个元素是一个pair<int,int>,也就是一个装两个int的小盒子。
//在这段代码里:盒子的first放数值nums[i],盒子的second放这个数值的原始下标i。

for (int i = 0; i < n; ++i) a[i] = {nums[i], i};
//遍历原数组,把每个"数值 + 原始下标"打包成一个pair,放进a里。
//比如nums = [2,7,11,15],a就是 {(2,0), (7,1), (11,2), (15,3)}。
//为什么要带上下标?因为下一步要排序,排序会打乱位置,原始下标必须跟着数值一起走,排完才能找回原来的位置。

sort(a.begin(), a.end());
//把数组a从小到大排序。pair排序规则是先比first(数值),first相同再比second(下标)。
//排完之后a按数值升序排列,但每个pair里还带着它的原始下标。

int l = 0, r = n - 1;
//两个指针:l指向数组最左边(最小值),r指向数组最右边(最大值)。准备从两头往中间夹。

while (l < r) {
//只要l还在r左边,就继续。l < r保证两个指针指向不同的元素,不会用同一个数两次。

    int s = a[l].first + a[r].first;
    //把左指针的数值和右指针的数值加起来,存进s。a[l].first是左边那个数,a[r].first是右边那个数。

    if (s == target) return {a[l].second, a[r].second};
    //如果两数之和正好等于target,找到了。
    //返回的是a[l].second和a[r].second,也就是这两个数的原始下标,不是l和r(l和r是排序后的位置)。

    if (s < target) ++l;
    else            --r;
    //如果和小于target,说明和太小,需要更大一点:因为数组升序,把l往右移一格,左边的数变大,和就增大。
    //否则(和大于target),说明和太大,需要更小一点:把r往左移一格,右边的数变小,和就减小。
}

return {};
//如果while走完都没返回,说明找不到任何一对数加起来等于target,返回空结果。
目录