LeetCode(1.两数之和[简单])—双指针,哈希表

题目

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

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

题目解析及优化

  1. 暴力解决法

    暴力解决法很好理解,逐步遍历所有的数组组合,找出答案。由于时间复杂度很高,就不贴代码了,太弟弟了。

  2. 排序+双指针法

    先将数组排好序,用两个指针分别指向开始结尾,开始往中间聚拢。如果两指针碰头,就说明没有找到答案。

    代码如下:

    class Solution {
    public:
        vector<int> twoSum(vector<int>& nums, int target) {
            //ans存储答案的两个数
            vector<int> ans;
            //temp存储排序之后的数组,
            //由于之后需要原数组的位置,不能对原数组直接排序
            vector<int> temp;
            temp=nums;
            int n=temp.size();
            //sort排序的方法推荐读者掌握,很多时候都可以用到
           sort(temp.begin(),temp.end());
           //两个下标指向开始和结尾
           int i=0,j=n-1;
           while(i<j)
               if(temp[i]+temp[j]>target){j--;}
              else if(temp[i]+temp[j]<target){i++;}
              else break; 
           }
           if(i<j){
           //如果两指针没有碰头说明有答案存在,与之前的下标对应
          for(int k=0;k<n;k++){
              if(i<n&&nums[k]==temp[i]){
                  ans.push_back(k);
                  i=n;
              }
             else if(j<n&&nums[k]==temp[j]){
                  ans.push_back(k);
                  j=n;
              }
              if(i==n&&j==n)return ans;
          }
          }
            return ans;
        }
    };
    
  3. 哈希表

    此方法需要哈希表的一些知识,可以点击此处

    我们是在知道和的情况下去找两个加数,那么自然可以用一个加数去找另一个加数,遍历一个数组的元素,如果和减去这个元素的值在另一个数组里面出现过,那么答案存在。这个方法明显比暴力解决法更简洁。

    在用哈希表的时候也有两种方法:

    • 用两次哈希表:第一次将数组的值和索引添加到表中,第二次遍历寻找每个元素以及target-nums[i],切记,同一个元素不能用两次。
    class Solution {
    public:
        vector<int> twoSum(vector<int>& nums, int target) {
            vector<int> ans;
            map<int,int> p;
            int l = nums.size();
            for(int i = 0;i<l;i++){
                //p.second是数字的下标+1,只要存在就不会为0
                p[nums[i]] = i+1;
            }
            for(int i = 0;i<l;i++){
                //用两个判断条件
                if(p[target-nums[i]]&&(i!=p[target-nums[i]]-1)){
                    ans.push_back(i);
                    ans.push_back(p[target-nums[i]]-1);
                    return ans;
                }
            }
            return ans;
        }
    };
    
    • 用一次哈希表:其实在上述的用两次哈希表的方法中,会有重复计算的部分,在找一个元素的时候,同时已经找了target-nums[i]的元素,两者选其一即可,在将数组添加到表中的时候,就可以直接寻找,不会影响答案,而且比上述方法更省时间。代码如下:

      class Solution {
      public:
          vector<int> twoSum(vector<int>& nums, int target) {
              vector<int> ans;
              map<int,int> p;
              int l = nums.size();
              for(int i = 0;i<l;i++){
                  
                  if(p[target-nums[i]]){
                      ans.push_back(i);
                      ans.push_back(p[target-nums[i]]-1);
                      return ans;
                  }
                  //将赋值放在判断之后就能避免判断同一元素的情况
                  p[nums[i]] = i+1;
              }
              return ans;
          }
      };
      

      提交之后,发现用时更短的人用的是unordered_map,由于底层原理的不同,unordered_map用的是哈希表,查找的时间复杂度为o(1)

心得及总结

  1. 暴力法并不是一无是处,有时候在需要时间换取空间的时候可以采取暴力法。(少见)
  2. 除了暴力法以外的几种方法,均采用了元素互补的思想:即两个元素之间有某种关系的时候,可以用一个元素去找另一个元素,而不用专门去找。
  3. 这个题目中是用一次哈希表时间胜出了,但是如果题目中是有序数组的话,用指针就会更优秀了。
  4. 在哈希表的使用过程中,并没有用到map的自动有序的特性,所以可以使用unordered_map去更节省时间。(充分利用不同数据结构的优点)

知识补充

  1. 三种map的优缺点
    在这里插入图片描述

    运行效率方面:unordered_map最高,hash_map其次,而map效率最低单提供了有序的序列。

    占用内存方面:hash_map内存占用最低,unordered_map其次(数量少时优于hash_map),而map占用最高。

    需要无序容器时候用unordered_map,有序容器时候用map。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值