【算法1】 两数之和

题目描述:(来源于乐扣1)

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

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例:
在这里插入图片描述


1 暴力解法

这道题和乐扣167题( 两数之和 II - 输入有序数组)类似,区别仅在于这道题的数组不是有序的。

暴力解法依然是双层遍历,然后比对计算两者之和。
从下面的程序可以看出,时间复杂度是 O ( n 2 ) O(n^2) O(n2)的。

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){
                if(nums[i]+nums[j]==target){
                    result.push_back(i);
                    result.push_back(j);
                    return result;
                }
            }
        }
        return result;
    }
};

运行结果:
上面的280ms是在if语句中加了return语句,因为题中说明了只会有一个答案,所以一旦找到一个,就可以提前返回。

下面的436ms是没有在if语句中加return语句,所以会遍历整个数组结束,耗时较长。
在这里插入图片描述

2 查找表法

如果我们打算采用类似于167号题的二分搜索法:固定一个元素,查找另一个元素,就必须要求数组是有序的。

我们可以先对数组排序,然后使用二分搜索法。但是问题在于,这道题要求返回的是索引,这样我们在排序时就需要记录元素的索引,比较麻烦。

查找表法:
将所有元素放入查找表中,之后对于每一个元素a,查找target-a是否存在。

1)使用set
使用set,我们就不能记录查找到的元素的索引,所以在找到答案时,我们需要遍历一次数组,查找其对应的索引。
这里我们使用底层哈希实现的set,即unordered_set

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> result;
        //将所有元素放入集合中,注意unordered_set的创建方式
        unordered_set<int> hs(nums.begin(),nums.end());
        
        for(int i=0;i<nums.size();++i){
            int number=target-nums[i];
            if(hs.find(number)!=hs.end()){
            	//如果找到答案,才会进行索引查找
                for(int j=i+1;j<nums.size();++j){
                    if(nums[j]==number){
                        result.push_back(i);
                        result.push_back(j);
                        return result;
                    }
                }
            }
        }
        return result;
    }
};

由于哈希在添加、查找方面的时间复杂度都是 O ( 1 ) O(1) O(1),所以能够大大降低之前的时间复杂度。

运行结果:
在这里插入图片描述

2)使用map
前面我们使用set最后需要遍历一次数组,找到答案对应的索引。但是我们使用map就可以存储元素对应的索引,即

键——元素;
值——索引;

但是,添加到map中的元素一旦有重复的值,就会发生覆盖现象。为了解决这个问题,我们不从一开始就将所有元素放入查找表中,而是每次将查找元素V的前面的元素放入查找表中。

如果在V(红色)前面有相同的元素等于V,此时待查找的另一个元素target-V如果在前面的数组中(蓝色部分),查找表中也不会有重复元素。如果没有,就将V(红色)添加进查找表中。
继续向后查找元素,就算前面的查找表有了重复元素,那也不会是我们要寻找的答案。因为,假设下一个元素是我们要找的答案,那个另一个元素如果是前面查找表中的那个重复元素,结果就会有3个元素,这与题目中两个元素相悖。

因此,采用局部放入查找表的方法可行。
Alt

实现:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> record;

        for(int i=0;i<nums.size();++i){
            int complement=target-nums[i];
            if(record.find(complement)!=record.end()){
            	//如果找到,就将索引放入数组中
                int res[2]={i,record[complement]};
                //这里返回直接将数组放入容器中,res+2表示最后一个元素的后一个位置
                return vector<int>(res,res+2);
            }
            record[nums[i]]=i;
        }
        throw invalid_argument("the input has no solution");
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值