代码随想录刷题-哈希表-两数之和

文章介绍了LeetCode中的第1题——两数之和,提供了暴力解法和哈希表优化的解决方案。暴力解法是双重循环,时间复杂度为O(n^2),而哈希表的使用可以将时间复杂度降低到O(n),通过存储数组元素及其索引来快速查找目标和。
摘要由CSDN通过智能技术生成

两数之和

本节对应代码随想录中:代码随想录,讲解视频:梦开始的地方,Leetcode:1.两数之和,学透哈希表,map使用有技巧!_哔哩哔哩_bilibili

习题

题目链接:1. 两数之和 - 力扣(LeetCode)

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

暴力解法

作为 LeetCode 的第一题,题意很明确,就是数组中找和为 target 的两个数,返回他们的下标,并且一定能找到这俩数,并且答案唯一(顺序可以不一样)。

最直接的就是两个 for 循环遍历一个数的时候看看剩余的数里面有没有满足和为 target 的

class Solution {
   public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int size = nums.size();
        vector<int> res;
        for (int i = 0; i < size; i++) {
            for (int j = i + 1; j < size; j++) {
                if (nums[i] + nums[j] == target) {
                    res.push_back(i);
                    res.push_back(j);
                    return res;
                }
            }
        }
        return res;
    }
};
  • 时间复杂度:O(n^2)。有两个循环,每个循环运行 n 次,所以总的运行次数为 n * n = n^2
  • 空间复杂度:O(n)。创建了长度为 n 的向量 res 来存储结果

可以优化的点

  • 其实不用新定义个 res,在返回的时候直接返回 return {i,j} 即可,这样空间复杂度就优化成 O(1)

哈希表

上面的暴力解法,就是先遍历一个数,然后再查找剩余的数中有没有 target-nums[i]。那就简化成了在数组中查找一个元素是否存在的问题,所以就可以使用哈希表,因为哈希表查找元素的时间复杂度为 O(1)。

而哈希表有3种:数组、set 和 map,只有 set 和 map 是有 O(1)的 find 函数的。但是 set 只存储 key,而题目要求返回的是下标,set 无法直接得到原来的数的下标。

STL 中有个 distance 函数可以计算两个元素之间的距离,计算找到的元素和 set.begin()之间的距离就可以获得下标吗?
答:这种方法无法获得原有的下标,使用 unordered_set 插入元素时不会保留原始顺序,而是根据哈希表中元素的内部顺序随机排序。也就是说,即使向 set 中添加与 vector 中相同的元素,它们在 set 中的顺序也可能与在 v1中的顺序不同。例如示例中的{2,7,11,15}在 unordered_set 中就会变成{11,7,15,2}的顺序

map 中只有 unordered_map 的查询效率为 O(1),那么 key 和 value 存什么呢?map 查找的时候查的是 key,而我们要找的是 target-nums[i]即查的是元素本身。那么 key 存的就是数组中的元素,而 value 存的就是对应的下标

我的写法如下

class Solution {
   public:
    vector<int> twoSum(vector<int>& nums, int target) {
        // 创建一个无序哈希表map,key为整数型,value为整数型
        unordered_map<int, int> map;
        // 遍历nums中的元素,将元素值作为键,元素下标作为值插入到map中
        for (int i = 0; i < nums.size(); i++) {
            map.insert({nums[i], i});
        }
        for (int i = 0; i < nums.size(); i++) {
            int tmp = target - nums[i];
            // 判断map中是否含有该差值,如果存在且满足条件则返回下标值
            if (map.find(tmp) != map.end()) {
                if (i != map[tmp]) {
                    return {i, map[tmp]};
                }
            }
        }
        return {};
    }
};
  • 时间复杂度:O(n)。其中,第一个 for 循环遍历整个 nums 数组,需要 n 次操作;第二个 for 循环也遍历整个 nums 数组,需要 n 次操作,哈希表map插入n个元素,需要n次操作。因此总的时间复杂度为O(n+n+n)=O(n)
  • 空间复杂度:O(n)。创建了一个哈希表,存储了n个元素,因此空间复杂度为O(n)

而看过题解后,上面的写法可以有优化的地方。我是先将所有元素插入到 map 中,而实际上没有必要先插入所有元素。只需要当没有 target - nums[i]的时候再将当前 nums[i]插入到 map 中,这样也避免了和自己匹配即2*nums[i]=target。如2,7,11,15,target=9,当 i=0时,虽然没有找到 map 中含有7。但是将2插入 map 后,当 i=1即去找 map 中是否含有2的时候就可以找到了。

class Solution {
   public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> map;
        for (int i = 0; i < nums.size(); ++i) {
            auto it = map.find(target - nums[i]);
            if (it != map.end()) {
                // 如果找到了,返回这两个元素的下标
                return {it->second, i};
            }
            // 否则将当前的数插入哈希表
            map[nums[i]] = i;
        }
        return {};
    }
};
  • 时间复杂度:O(n)。其中n是nums数组中元素的个数,因为只需要遍历一次nums数组就能找到符合条件的目标值两个元素
  • 空间复杂度:O(n)。在最坏情况下,所有的元素都需要插入哈希表中,所以哈希表的空间占用和nums数组大小相同
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值