初识哈希(数组、set、map)

 总的纲领:当我们需要快速查找一个元素是否在集合中,考虑用哈希法

题目:242. 有效的字母异位词

题意:给定两个字符串,判断两个字符串中字母是否一样 

思路1:暴力法 —— O(nlgn) 

将两个字符串排个序,比较一下,相等就返回 true,要不然就返回 false

class Solution {
public:
    bool isAnagram(string s, string t) {
        sort(s.begin(), s.end());
        sort(t.begin(), t.end());
        if(s != t) {
            return false;
        }
        return true;
    }
};

 思路2 :哈希法

可以用一个数组来做哈希,这个数组用来统计字符串中出现的字母的个数,数组下标 就是 s[i] - 'a' ,这个就是一种映射;

只用一个数组就可以了,统计第一个字符串的字母个数就 做 ++ 的操作,第二个字符串的字母个数就做 -- 的操作;

最后如果这个数组中有某个下标对应的值不是0 ,那就说明 两个字符串不相等 return false;

这个是第一次自己写的代码,当时没想过这就是哈希的方法,但是就是用了两个数组,其实没有必要,而且当时还被卡住 debug了一会;

class Solution {
public:
    bool isAnagram(string s, string t) {
        bool res;
        vector<int> cnts(26,0);
        vector<int> cntt(26,0);
        for(int i = 0; i < s.size(); i++) {
            cnts[s[i] - 'a']++; 
        }
        for(int j = 0; j < t.size(); j++) {
                cntt[t[j] - 'a']++;
            }
      /* for(int i =0;i<cnts.size();i++) {
           cout<<cnts[i]<<' ';
       }
       cout<<endl;
       for(int i =0;i<cnts.size();i++) {
           cout<<cntt[i]<<' ';
       }*/
        for(int i = 0; i < cnts.size(); i++) {
            if(cnts[i] == cntt[i]) {
                res = true;
               continue;
            } else if(cnts[i] != cntt[i]){
                res = false;
                break;
            }
        }
        return res;
    }
};

 相反,这个就比较看着清爽舒服

class Solution {
public:
    bool isAnagram(string s, string t) {
        vector<int> res(26,0);
        for(int i = 0; i < s.size(); i++) {
            res[s[i] - 'a']++;
        }
        for(int i = 0; i < t.size(); i++) {
            res[t[i] - 'a']--;
        }
        for(int i = 0; i < res.size(); i++) {
            if(res[i] != 0) {
                return false;
            } 
        }
        return true;
    }
};

题目:349. 两个数组的交集

题意:给两个数组,找两个数组中相同的元素

 思路1:暴力法——O(n^{2})

第一次自己写出来的,当时没考虑到还要去重,特地去查了一下vector去重的操作;

两层for循环,一个一个找,如果两个数组中有相等的元素,就插入,最后去重,返回答案;

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        vector<int> res;
        vector<int> res1;
        for(int i = 0; i < nums1.size(); i++) {
            for (int j = 0; j < nums2.size(); j++) {
                if (nums1[i] == nums2[j]) {
                    res.push_back(nums1[i]);
                    break;
                }
            }
        }
        //下面两行 vector 去重操作
        sort(res.begin(), res.end());
        res.erase(unique(res.begin(), res.end()), res.end());
        return res;
    }
};

思路2:哈希法——时间复杂度 O (n)

这里我们选择用 set 来哈希,因为题目没有给出数的范围,用数组来哈希映射,会浪费空间; 

这里我们还要考虑去重,unordered_set的底层实现是哈希表, 使用unordered_set 读写效率是最高的,并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set。

接下来:我们先对一个数组进行去重(以nums1为例),让nums1去重后,以他为标杆,去遍历nums2中的元素,如果找到了,那就插入到答案中,最后返回即可;

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> nums1_size (nums1.begin(), nums1.end());
        unordered_set<int> res;
        for (int i = 0; i < nums2.size(); i++) {
            if (nums1_size.find(nums2[i]) != nums1_size.end()) {
                res.insert(nums2[i]);/*find函数 ,如果找到元素,则返回指向该元素的迭代器,
否则返回指向集合末尾的迭代器,即set :: end();还有 set 中插入元素是insert;*/
            }
        }
        return vector<int>(res.begin(),res.end());
    }
};

题目:202. 快乐数

题意:

编写一个算法来判断一个正整数 n 是不是快乐数。

「快乐数」 定义为:

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

思路:哈希 用 set 来做 

class Solution {
public:
    int getsum(int n) {
        int sum = 0;
        while(n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }  
        return sum; 
    }
    bool isHappy(int n) {
    unordered_set<int> res;
    while(1) {
        int sum = getsum(n);
        if(sum == 1) {
            return true;
        } 
        if(res.find(sum) != res.end()) {
            return false;
        } else {
            res.insert(sum);
        }
        n = sum;
    }
 }
};

题目:1. 两数之和

题意:给一个数组,一个target,数组中是否有两个数之和= target ,返回两个数的下标

 思路:我们需要知道 值 和 下标,所以就需要 用 map 来做哈希,map 存的是 遍历过的元素

然后我们开始遍历,查询 target - nums[i] 是否在之前出现过, 如果出现过,就返回下标(一个是 当前数的小标 i,一个是 map 里的 iter-> second),如果没出现过,就把这个数插入到我们的map 中;整个数组遍历完后,没有符合条件的 那么就返回空 return {};

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

 几点思考:

1.为什么要用哈希?

哈希:是用最快的时间来解决一个数是否在一个集合中出现过;

对于本题:我们是去查target - nums[i] 在之前是否出现过,所以我们可以用哈希;

2.为什么要用map来哈希?

本题需要知道具体的数值,最后还需要返回下标,需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适;

为什么key 存放元素,我们查找的是元素有没有出现过,map就是能在最快的时间内查找key,所以我们让元素值等于 key

3.三种map的结构区别?

std::unordered_map 底层是哈希表,读和存的效率是最高的为O(1);std::map 和std::multimap 的底层实现是红黑树。

4.map 在本题里的作用是什么?

map 是用来存 我们已经遍历过的元素

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值