算法力扣刷题记录 十六【1. 两数之和】

前言

哈希篇,继续下一题。

记录 十六【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 <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案

进阶:

你可以想出一个时间复杂度小于 O(n ^2) 的算法吗?

二、尝试解答

思路和实现

我们先不考虑这个题目是出现在哪个篇章的,出现在固定篇章会先知解答方法,只从题目本身来想。

(1)这是一个数组,处理数组:学过的方法有双指针法、滑动窗口法、直接遍历(暴力解法)。

  • 双指针/滑动窗口试一下:题目让返回两个数之和=target,这两个数的下标。如果用快慢指针,滑动窗口这代表的是一个区间,记录求和得到区间内的元素之和,不是两个元素相加。

  • 直接遍历(暴力解法),肯定可以。

    cpp 复制代码
    class Solution {
    public:
        vector<int> twoSum(vector<int>& nums, int target) {
            vector<int> result;
            for(int i=0;i < nums.size();i++){
                for(int j = i+1;j < nums.size();j++){   //为什么是j=i+1,因为同一个元素不让重复出现
                    if(nums[i] + nums[j] == target){
                        result.push_back(i);
                        result.push_back(j);
                        break;
    
                    }
                }
            }
            return result;
        }
    };

(2)ok,已经用一种方法实现,可以考虑另外办法?上面的遍历时间复杂度O(n^2)。进阶:如何减小时间复杂度?

答:肯定要遍历数组,只遍历一次数组就会减少时间复杂度,用i得到某个数组的值,接下来要找(target-nums[i])有没有在数组中存在过? "找一个元素有没有在集合中存在过"------哈希法

  • 第一步:确定用什么哈希结构:

    • 数组?:本来就是数组,用下标作为key,可是目标是得到下标key,所以数组不行。
    • set?:输入数组元素可能重复(示例3),所以multiset,key是单个,如果放数组下标,那么nums[i]没有存;如果放nums[i],那么对应下标是什么?不知道。所以multiset也不行。
    • map?:元素可重复,肯定是multimap;map有key还有mapped-value,可以存key对应的value。下标和nums[i]都能访问到,好像可以,确定用multimap
  • 第二步:确定什么作为key,什么是mapped-value?

    • 如果数组下标作为key,但是multimap的find(k),是从key来得到mapped-value。题目要求的是下标。所以不行。
    • 那么nums[i]元素值所以key,关联key的mapped-value是对应下标i。
  • 代码实现

    cpp 复制代码
    class Solution {
    public:
        vector<int> twoSum(vector<int>& nums, int target) {
            multimap<int,int> map;//哈希结构,先定义容器,可重复。
            vector<int> result;//返回值先摆上
            for(int i = 0;i < nums.size();i++){	//遍历数组,把键值对放进去
                map.insert({nums[i],i});	//用元素值作为key,用下标i作为value
            }
    
            for(auto it = map.begin();it != map.end();++it){	//在容器map中找,用begin()和end()可以遍历所有对象。
                auto another = map.find(target - it->first); //找另一个加数
                //multimap<int,int>::iterator another = map.find(target - it->first);//auto可以换成:multimap<int,int>::iterator,用auto,让自动识别类型。
                if(another != map.end() && it != another){ //找到并且不是it本身,把下标value读出来放到result中
                    result.push_back(it->second);	
                    result.push_back (another->second);
                    break;//找到之后可以退出
                }
            }
    
            return result;
        }
    };

代码随想录学习

学习内容

(1)思路区别:虽然都是哈希法,但是处理方法不同。

上面用哈希结构multimap,先把数组所有内容都放到multimap中,在multimap里面遍历看能不能找到两个加数?
  • 学习到的思路,边遍历边找。把遍历过的元素加入到容器中,遍历到某个元素,看另一个加数有没有存在过。

  • 新思路的代码实现:

    • 确定结构:还是map,但是这里可以用unordered_map。解释:无序------只是看之前遍历到的nums[i]是不是当前元素的另一半加数,顺序不是那么重要。不重复------虽然nums可能有重复元素,但是相同元素key有一个代表在容器unordered-map中存在就可以了,反正key一样,返回哪个value下标值都可以。

    • 用unordered-map,效率还能更高,相比于multimap。

      cpp 复制代码
      class Solution {
      public:
          vector<int> twoSum(vector<int>& nums, int target) {
              unordered_map<int,int> map;//放遍历过的元素及下标
              vector<int> result;
              for(int i = 0;i < nums.size();i++){
                  unordered_map<int,int>::iterator it = map.find(target-nums[i]);
                  if(it != map.end()){//找到另一个加数
                      result.push_back(i);
                      result.push_back(it->second);
                      break;  //找到就可以退出循环,返回结果。
                  }
                  map.insert({nums[i],i});	//没找到
              }
              return result;
          }
      };
  • 对比参考代码

      在插入的时候:都可以通过,但是使用的函数原型不一样而已。
      参考给的:map.insert(pair<int, int>(nums[i], i)); //先创建pair对象,再把对象作为参数给insert。那么函数原型是:template <class P>    pair<iterator,bool> insert ( P&& val );
      个人实现:map.insert({nums[i],i});//使用初始化列表的方式,函数原型:void insert ( initializer_list<value_type> il );
    

总结:参考给的思路:效率可以更高。


总结

(欢迎指正,转载表明出处)

相关推荐
卡戎-caryon1 小时前
【数据结构】06.栈&&队列
c语言·数据结构·算法·链表
QD_ANJING1 小时前
2024年前端面试中面试官常拷打的“项目细节”!
前端·面试·职场和发展
山脚ice1 小时前
【CT】LeetCode手撕—704. 二分查找
算法·leetcode
国中之林1 小时前
【qt】如何获取网卡的IP地址?
服务器·c++·qt·网络协议·学习·tcp/ip
一缕阳光a1 小时前
2024年6月份找工作和面试总结
面试·职场和发展
贱贱的剑2 小时前
【算法】选择排序
算法·rust·排序算法
瑜陀2 小时前
2024.06.30 刷题日记
数据结构·算法·leetcode
Star Patrick2 小时前
*算法训练(leetcode)第二十天 | 39. 组合总和、40. 组合总和 II、131. 分割回文串
c++·算法·leetcode
光久li2 小时前
【算法刷题 | 动态规划14】6.28(最大子数组和、判断子序列、不同的子序列)
算法·动态规划
飘然渡沧海2 小时前
gbk,ucs-2转中文
java·开发语言·算法