LeetCode——两数之和

218 阅读1分钟

题目描述:

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

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

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

方法一:暴力求解

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

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

        return {};
    }
};

暴力求解方法的时间复杂度为O(n^2),通过两次遍历进行排查,最终获得正确答案后进行返回,由于给出的示例都是存在答案的,因此代码块的return {} 操作只是为了满足函数在语法上的需求。

执行用时以及内存消耗

方法二:使用两次map(非哈希表)

在C++中,map一般采用红黑树(RB Tree)进行存储,而hash_map采用哈希表进行存储。

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

        map<int,int> map;
        for (int i=0; i<nums.size(); i++){
            map[nums[i]] = i;
        }

        for (int j=0; j<nums.size(); j++){
            if (map.count(target-nums[j])==1 && map[target-nums[j]]!=j){
                return {j,map[target-nums[j]]};
            }
        }

        return {0,0};

    }
};

第一次使用map对数组nums以(元素值,下标)的方式进行存储,在初次遇到这种方式的存储时,我脑海中有一个顾虑:要是元素值相同的话,那应该怎么进行存储?或者说以这种方式进行存储,是否存在示例使得该代码无效?但在仔细审题后发现,若是数组当中存在若干个相同元素,则只存储第一个元素的下标,这样生成的答案其实是满足题目要求的。例如[5,2,4,2]且target为6,根据这个算法返回的数组为{1,2}是正确的。

第二次使用map是为了寻找正确的下标组合:

map.count(target-nums[j])==1

该判定条件是为了寻找当前下标元素值所对应满足target的键值,若存在该键值则count函数会返回1,例如[5,7,4,9],target=16,当下标j=1时,target-nums[j]=9,由于第一次使用map是完成对元素值的存储,则此时会count函数会返回1满足条件。

map[target-nums[j]]!=j

该判定条件是为了满足题目当中所说的数组中同一个元素不能使用两遍,例如[1,2,2,8],target=4,当下标j=1时,此时target-nums[j]=2,则map[target-nums[j]]=1,与下标一致不符合判断条件,而当下标自增1变为2时,此时map[target-nums[j]]=1与当前下标不一致,则满足判断条件,判断条件内部的返回值{2,1}为题目所要求。

代码块最后的返回值{0,0}也是为了满足函数在语法上的需求。

执行用时以及内存消耗

方法三:使用一次map(非哈希表)

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

        map<int,int> map;

        for(int i=0; i<nums.size(); i++){
            if(map.find(target-nums[i])!=map.end()){
                return {map[target-nums[i]],i};
            } else {
                map[nums[i]] = i;
            }
        }

        return {0,0};

    }
};

map.count()与map.find()的区别                                                                                  1、map.count()在找到目标key值后会返回1,否则返回0;                                              2、map.find()在找到目标key值后会返回位置,否则返回map.end()。

该方法将 数组元素值的存储 以及 寻找满足target的元素下标对 结合在一起,对元素值进行存储的同时,查找map当中是否已经存在匹配的元素,这样就巧妙地省去了对是否为同一下标的检查。

执行用时以及内存消耗

总结:

从这三个方法来看,暴力求解以及map的使用,就思想而言还是固定一个元素值,寻找匹配它的目标元素值,但耗时从652ms降到20ms,如此大的速度效率提升,背后应该是红黑树(RB Tree)以及哈希表(Hash)提供的快捷搜索功劳,因此,对这两种数据结构进行更深的学习理解应该是十分有必要的。