再见力扣第一题(两数之和)

题干:

. - 力扣(LeetCode)

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(n2) 的算法吗?

双指针思路

第一次见到这个题,我感觉自己有一个还不错的想法,那就是双指针:

先把nums从小到大排序,这样target对应的 两数之和 组合就可以从两头直接遍历

这样为什么不会出现遗漏呢?为什么不会少情况呢?

最终的组合是一大一小两个数,排列过后的数组从小到大排列。我们只需从两头开始遍历,不妨叫左边的数为小数,右边的数为大数。

如果和大于target,那一定是大数大了而不可能是小数小了,所以要把大数向左遍历;

同理,和小于target,我们就应该向右遍历小数;

这样一来,我们只需要遍历小于等于N(length)的长度,就能锁定两个目标,而如果左右两边都遍历一遍后找不到,就可以断定答案不存在。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        int i=0,j=nums.size()-1;
        while(i<j){
            int sum=nums[i]+nums[j];
            if(sum>target){
                --j;
                continue;
            }
            else if(sum<target){
                ++i;
                continue;
            }
            else{
                return {i,j};
            
            }
        }
        return{};
    }
};

(这个代码不对是因为返回的是排序之后的指针)

但是,第一次思考的时候,有一点我没有注意,那就是这种方法可以很方便找到目标值,但却不容易轻松找到两个值原来的指针。

而更为方便的就是主流的解法哈希表思路

哈希

哈希的思路就是创造索引和查询

直接开始遍历数组,每遍历到一个数,就看字典里有没有对应的 两数之和组合,如果没有,就把这个数添加到map里,等待之后的遍历来匹配。

对于新手而言哈希表有两个缺点,如果你只学过c,那么你又要亲自动手实现键值对结构体的的创建(map,pair),还有相关插入删除操作的定义,稍微有一些枯燥麻烦;

typedef struct {
     int key;
     int value;
     UT_hash_handle hh; // make this structure hashable
 } map;

map* hashMap = NULL;

 void hashMapAdd(int key, int value){
     map* s;
     // key already in the hash?
     HASH_FIND_INT(hashMap, &key, s);
     if(s == NULL){
         s = (map*)malloc(sizeof(map));
         s -> key = key;
         HASH_ADD_INT(hashMap, key, s);
     }
     s -> value = value;
 }

map* hashMapFind(int key){
     map* s;
     // *s: output pointer
     HASH_FIND_INT(hashMap, &key, s);   
     return s;
 }

 void hashMapCleanup(){
     map* cur, *tmp;
     HASH_ITER(hh, hashMap, cur, tmp){
         HASH_DEL(hashMap, cur);
         free(cur);
     }
 }

 void hashPrint(){
     map* s;
     for(s = hashMap; s != NULL; s=(map*)(s -> hh.next)){
         printf("key %d, value %d\n", s -> key, s -> value);
     }
 }

而如果你想使用cpp的话,那你就要熟悉一下unordered_map, pair的用法,这种结构体第一次见可能会不知道怎么用。

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

我第一次见这种代码的时候,有几点让我比较陌生:

iter 的使用,find(不知道查询的是<int,int>里面的哪一个int),insert的工作原理(map结构插入的结构是pair)

但是要熟练应用哈希法,掌握map,set等数据结构是必须的,还要多多接触,多多练习

附录

unordered_map:

声明和初始化

声明一个 std::unordered_set 并初始化。

cpp

std::unordered_set<int> mySet; // 空集合 std::unordered_set<int> mySet = {1, 2, 3, 4, 5}; // 使用初始化列表

插入元素

使用 insert() 方法插入元素。

cpp

mySet.insert(6); // 插入一个元素

检查元素是否存在

使用 find() 方法或 count() 方法检查元素是否存在。

cpp

if (mySet.find(3) != mySet.end()) { // 元素3存在 } // 或者使用 count 方法 if (!mySet.count(3)) { // 元素3不存在 }

删除元素

使用 erase() 方法删除元素。

cpp

mySet.erase(3); // 删除元素3

清空集合

使用 clear() 方法清空集合。

cpp

mySet.clear();

获取集合大小

使用 size() 方法获取集合的大小。

cpp

std::cout << "Set size: " << mySet.size() << std::endl;

遍历集合

可以使用迭代器遍历 std::unordered_set

cpp

for (auto it = mySet.begin(); it != mySet.end(); ++it)

{ std::cout << *it << " "; }

或者使用范围基 for 循环:

cpp

for (auto elem : mySet) { std::cout << elem << " "; }

使用自定义哈希函数

如果你使用自定义类型作为 std::unordered_set 的元素,可能需要提供自定义的哈希函数。

cpp

struct Point { int x, y; }; namespace std { template<> struct hash<Point> { size_t operator()(const Point& p) const { return hash<int>()(p.x) ^ hash<int>()(p.y); } }; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Monster_Prince

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值