走走看看到处都 值得学习.|

冰镇杨梅

园龄:2年2个月粉丝:0关注:0

2023-01-16 20:55阅读: 130评论: 0推荐: 0

代码随想录算法训练营第六天 哈希法 | 242.有效的字母异位词 | 349. 两个数组的交集 | 202. 快乐数 | 1. 两数之和

哈希表

哈希表适用于快速判断元素是否存在于表中,针对于哈希碰撞,有拉链法和线性探测法

拉链法

碰撞的元素被储存在链表中,拉链法需要根据数据规模选择适当的表大小,既不造成表内大量空值浪费内存,又不会产生过多碰撞使链表过长

线性探测法

线性探测法需要哈希表大小一定大于数据规模,这样在碰撞时可以向下查找到一个表中空位来解决碰撞问题。

代码内的分支

做题的时候可能会遇到使用数组,set和map三种情况。
简单来说只存键可以用set,要存键值对可以用map,数组适用于数据范围可控且较小的情况(例如只包含26个小写字母)。
set和map又可以分成是不是multi(键可重复),是不是unordered(底层使用哈希表,其他情况为有序key,底层使用红黑树)

哈希数组 lc242 有效的字母异位词

能用数组的时候用数组可以稍微提升性能,毕竟set里要做哈希映射,结构也会比数组复杂一点

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

unordered_set lc349 两个数组的交集

这道题目因为限制了数组内最大数字不超过1000,所以也可以用数组来做。注意题目要求的范围是0 <= n <= 1000,所以一共有1001个数字,声明哈希数组时大小最小是1001。

下面是使用set的解法。set还可以去重,注意不要使用multiset

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set;
        unordered_set<int> n1(nums1.begin(),nums1.end());
        for (int num:nums2){
            if (n1.find(num)!=n1.end()){
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(),result_set.end());
    }
};

unordered_set lc202 快乐数

本题关键在于判断循环的出现,根据题意不难发现只要某次位平方求和后的结果出现过且不为1,那么就会陷入死循环并应该返回false。这里要第一时间意识到判断一个数是否曾经出现在记录中很符合哈希法的应用场景。本题内数据范围不太可控且较大,使用unordered_set即可。

剩下的逻辑就是对快乐数计算的模拟,其中有一个小难点是把每一位(个位,十位,百位等)提取出来进行运算,看下面的代码应该就能明白如何实现了。

自己写的版本是堆在一起的,代码随想录里给的答案把一部分代码整合成了一个函数,逻辑完全一样。

放一起

class Solution {
public:
    bool isHappy(int n) {
        unordered_set<int> record;
        while(n != 1){
            int result = 0;
            while(n){
                result += (n % 10) * (n % 10);
                n /= 10;
            }
            /*
            for (int num : record){
                cout<<num<<' ';
            }
            cout<<endl;
            */
            if (record.find(result) != record.end()){
                return false;
            }
            record.insert(result);
            n = result;
        }
        return true;
    }
};

代码随想录答案-函数打包

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> set;
        while(1) {
            int sum = getSum(n);
            if (sum == 1) {
                return true;
            }
            // 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
            if (set.find(sum) != set.end()) {
                return false;
            } else {
                set.insert(sum);
            }
            n = sum;
        }
    }
};

unordered_map lc1 两数之和

这道题很适合用map来做,因为要用值做判断并返回一个下标,如果用set可能要存有序的,时间上感觉会慢一点。

除了数据结构上的知识,这道题还需要对c++语法有一定了解。比如迭代器,泛型算法,隐式转换等。当初第一次看这道题就是差了一些c++语法的知识,导致理解不够深刻。

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

本文作者:冰镇杨梅

本文链接:https://www.cnblogs.com/frozenwaxberry/p/17056284.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   冰镇杨梅  阅读(130)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起