【LeetCode哈希表#4】梦开始的地方:两数之和(map),以及关于容器map的一些代码技巧

两数之和

力扣题目链接(opens new window)

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

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

示例:

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

因为 nums[0] + nums[1] = 2 + 7 = 9

所以返回 [0, 1]

思路

暴力法

两层for循环,一个一个加起来然后判断呗

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        for(int i = 0; i < nums.size(); i++){
            for(int j = i + 1; j < nums.size(); j++){
                if(nums[i] + nums[j] == target){
                    return {i, j};
                }
            }
        }
        //不满足条件返回空
        return {};  
    }
};
哈希法

没错,这个题也可以用哈希法去解

只不过这里我们使用的哈希结构有所不同,先分析一下解题思路

题目要求我们找到数组中两个数,这两个数加起来要等于输入的target值

假设,现在遍历到数组的第一个数 A

C = target - A;

那么如果数组中存在 C 的话,我们就找到了两个符合条件的数{A, B},返回两者的下标即可

因此,我们需要有一个数据结构去存放已经遍历过的值和该值在数组中的下标

这是一个key-value的结构,对应到c++中可以通过map容器实现

C++中map,有三种类型:

映射 底层实现 是否有序 数值是否可以重复 能否更改数值 查询效率 增删效率
std::map 红黑树 key有序 key不可重复 key不可修改 O(log n) O(log n)
std::multimap 红黑树 key有序 key可重复 key不可修改 O(log n) O(log n)
std::unordered_map 哈希表 key无序 key不可重复 key不可修改 O(1) O(1)

std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。

因为本题并不需要key有序,所以使用unordered_map效率更高

那么key对应什么?value又对应什么呢?

我们想知道的是,某个值(当前遍历值与target作差之后的差值)是否被遍历过,即查找的对象是某个值,因此需要将遍历得到的数组元素值作为key,其下标作为value

流程

1、遍历数组,向unordered_map中查询当前遍历值对应的值是否被遍历过

2、如果查询到匹配值,返回该值对应的value,与当前遍历值的index组成一对结果

代码

主要障碍是对c++里面一些容器的操作不熟练(比如这里的map容器),这里直接粘的卡哥的代码

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        //声明一个unordered_map
        std::unordered_map <int,int> map;
        for(int i = 0; i < nums.size(); i++) {
            // 遍历当前元素,并在map中寻找是否有匹配的key
            // auto用于自动给迭代对象一个相应的数据类型
            auto iter = map.find(target - nums[i]); //这里是定义了一个map的iterator吗?没见过这种写法
            //end()指向的最后一个元素的后一个位置
            //iter != map.end()是什么意思?其常被用于map容器遍历时的判断条件
            //
            if(iter != map.end()) { //找到了
                /*
                (*it).first会得到key,
				(*it).second会得到value。
				这等同于it->first和it->second
				*/
                //{匹配值下标, 当前遍历值的下标}
                return {iter->second, i};
            }
            // 如果没找到匹配对,就把访问过的元素和下标加入到map中
            map.insert(pair<int, int>(nums[i], i)); 
        }
        return {};
    }
};

后续需要对c++容器的使用做强化训练(专题:c++刷题常用容器简介,TBD)

关于iter != map.end()

这里需要从它的上一行代码开始说起

摘自《C++ Primer Plus第6版》:

C++11新增了一个工具,让编译器能够根据初始值的类型推断变量的类型。为此,它重新定义了auto的含义。auto是一个C语言关键字,但很少使用,有关其以前的含义,请参阅第9章。在初始化声明中,如果使用关键字auto,而不指定变量的类型,编译器将把变量的类型设置成与初始值相同:

那么auto iter = map.find(target - nums[i]);的含义是:

定义一个 迭代器iter 用于在 map 中寻找 target - nums[i]

如果 map.find(target - nums[i]) 等于 map.end() ,那么键就没有找到

否则,find会返回一个指向找到的元素的迭代器

上述题解中的代码可写为:

std::unordered_map <int,int> map;
unordered_map <int,int> ::iterator it = map.find(target - nums[i]);

if (i != m.end()) { /* 找到了, 此时i->first就是匹配值下标(key), i->second是当前遍历值的下标(value) */}
else {  /* 没找到f */ }

参考:https://stackoverflow.com/a/1939962/15272780

二刷

简化了之前的代码

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> hashmap;
        for(int i = 0; i < nums.size(); ++i){
            if(hashmap.find(target - nums[i]) != hashmap.end()){
                return {hashmap[target - nums[i]], i};
            }
            hashmap.insert(pair<int, int>(nums[i], i));
        }
        return {};
    }
};

有一个关键点是:我们是使用遍历值与target作差,差值作为key,下标作为value

所以可以通过差值查询“差值是否在map中”

posted @ 2023-02-07 22:31  dayceng  阅读(98)  评论(0编辑  收藏  举报