面试算法之哈希专题

赎金信

在这里插入图片描述

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        // 小写字母
        int r_cnt[26];
        int m_cnt[26];
        for(int i = 0; i< magazine.size(); i++) {
            m_cnt[magazine[i]-'a']++; // 统计
        }
        // 对比

        for(int i = 0; i< ransomNote.size(); i++) {
            if(m_cnt[ransomNote[i]-'a']) {
                m_cnt[ransomNote[i]-'a']--;
            } else {
                return false;
            }
        }
        return true;
    }
};
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        // 小写字母
        int r_cnt[26];
        int m_cnt[26];
        for(int i = 0; i< magazine.size(); i++) {
            m_cnt[magazine[i]-'a']++; // 统计
        }
        // 对比

        for(int i = 0; i< ransomNote.size(); i++) {
            if(m_cnt[ransomNote[i]-'a']) {
                m_cnt[ransomNote[i]-'a']--;
            } else {
                return false;
            }
        }
        return true;
    }
};

同构字符串

在这里插入图片描述

class Solution {
public:
    bool isIsomorphic(string s, string t) {
        unordered_map<char,char> s_map;
        unordered_map<char,char> t_map;

        int s_size = s.size();
        int t_size = t.size();
        if(s_size != t_size) {
            return false;
        } 
        for(int i = 0; i< s_size; i++) {
            char x = s[i];
            char y = t[i];
            if((s_map.count(x) && s_map[x] != y) || (t_map.count(y) && t_map[y] != x)) {
                return false;
            }

            s_map[x] = y;
            t_map[y] = x;
        }
    return true;    
    }
};

收获

了解了一下 有关于 unordered_map 中 count 函数的使用
s_map.count(x) 就是 键为 x 的个数

逐步解析
在这里插入图片描述

方法二
思路

通过 match 建立关系
通过 count 记录 t[i] 是否已经有映射

这部分是表示, 不存在 s[i] 的映射, 但是发现 t[i] 已经做好映射了 ( count[t[i]] > 0 ) 直接返回 false

  			if (match[s[i]] == '\0') { // 如果当前字符没有映射关系
                if (count[t[i]] == 0) { // 如果当前字符在t中没有出现过
                    match[s[i]] = t[i]; // 建立映射关系
                    count[t[i]] = 1; // 将该字符在t中的出现次数加1
                }
                else
                    return false; // 如果当前字符在t中已经出现过,则返回false
            }

在这里插入图片描述
图解
在这里插入图片描述

class Solution {
public:
    bool isIsomorphic(string s, string t) {
        unordered_map<char, char> match; // 用于存储字符之间的映射关系
        unordered_map<char, int> count; // 用于记录字符在t中出现的次数
        
        for (int i = 0; i < s.size(); i++) {
            if (match[s[i]] == '\0') { // 如果当前字符没有映射关系
                if (count[t[i]] == 0) { // 如果当前字符在t中没有出现过
                    match[s[i]] = t[i]; // 建立映射关系
                    count[t[i]] = 1; // 将该字符在t中的出现次数加1
                }
                else
                    return false; // 如果当前字符在t中已经出现过,则返回false
            }
            else { // 如果当前字符已经有映射关系
                if (match[s[i]] != t[i]) { // 如果映射关系不正确
                    return false; // 返回false
                }
            }
        }
        return true; // 所有字符都满足同构条件,返回true
    }
};
class Solution {
public:
    bool wordPattern(string pattern, string s) {
        unordered_map<string,char> s_patterMap;
        unordered_map<char, string> pattern_sMap;

        // 遍历
       int p_len = pattern.size();
       int left = 0;
       int right = 0;
       for(int i = 0; i< p_len ; i++) {
            if(left >= s.size()) {
                return false;
            }
            // 遍历
            while(right < s.size() && s[right] != ' ') right++;
            // 右边界 right 
            string str = s.substr(left, right-left);
            
            if(s_patterMap.count(str) && s_patterMap[str] != pattern[i]) {
                return false;
            }
            if(pattern_sMap.count(pattern[i]) && pattern_sMap[pattern[i]] != str) {
                return false;
            }

            s_patterMap[str] = pattern[i];
            pattern_sMap[pattern[i]] = str;
            left = right + 1;
            right = left;
        }
        if(right < s.size()) {
            return false;
        } else {
            return true;
        }
    }
};

有效的字母异位词

在这里插入图片描述
方法一: 使用长度为 26 的数组对 小写字母进行统计

class Solution {
public:
    bool isAnagram(string s, string t) {
        // 用数组统计字母次数就可以了
        int s_char[26];
        int t_char[26];
        for(int i = 0; i< s.size(); i++) {
            s_char[s[i] - 'a']++;
        } 
        for(int i = 0; i< t.size() ; i++) {
            t_char[t[i] - 'a'] ++;
        }
        for(int i = 0; i< 26; i++) {
            if(s_char[i] != t_char[i]) {
                return false;
            }
        }
        return true;
    }
};

方法二: 使用 unordered_map 进行统计

  • 对比字符串长度
  • 记录一个字符串字母组成
  • 遍历另外一个字符串, 然后做减法
class Solution {
public:
    bool isAnagram(string s, string t) {
        unordered_map<char,int> s_char;
        // 长度对比
        if(s.size() != t.size()) {
            return false;
        }

        for(int i = 0; i< s.size(); i++) {
            s_char[s[i]]++;
        } 
        

        // 遍历 t 字符串
        for(auto c : t){
            if(--s_char[c] < 0) return false;
        }   
        return true;
    }
};

49. 字母异位分组

在这里插入图片描述
注意:

这里有个很好的思路, 如果是相同字母组成的单词, 对单词进行排序后, 肯定是相等, 所以通过遍历,然后对单词排序,
然后加入到 unordered_map<string, vector> 中就 , 后面就是对 unordered_map 进行遍历

要点
① 对一个单词进行排序

string str = "afdaf";
sort(str.begin(), str.end())

② 对 unordered_map<string , vector> 操作
加入

mp[key].emplace_back(strs[i]);

③ 对 unordered_map<string, vector> 遍历操作

for(auto pair: mp) {
	vector<string> values = pair.second;
	for(auto value: values) {
		cout<<value<<endl;
	}
	// ......
}
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        // 分组
         vector<vector<string>> result; // 结果
        if(strs.size() == 0  || strs.size() == 1) {
            result.push_back(strs);
            return result;
        }
        // 如果是同一组字母组成的单词, 排序后 可以画上等号
        unordered_map<string, vector<string>> mp; // 表示一种组合 所有单词 后面表示字符数组
        for(int i = 0; i< strs.size() ; i++) {
            string key = strs[i];
            sort(key.begin(), key.end());
            mp[key].emplace_back(strs[i]);
        }
        // 遍历 这个 map
        for(auto pair: mp) {
            result.emplace_back(pair.second);
        }
        return result;
    }
};

两数之和

在这里插入图片描述

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

        for(int i = 0; i< nums.size(); i++) {
            if(value.find(target - nums[i]) != value.end()) {
                auto it = value.find(target - nums[i]);
                return {it->second,i};
            }
            value[nums[i]] = i;
        }
        return {};
    }
};

思考: 如果不止一种结果,代码又是如何写呢???

存在重复元素 II

在这里插入图片描述

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
       unordered_map<int,int> contain_map;
       int length = nums.size();
       for(int i = 0; i< length; i++) {
        // 如果之前存在这个数, 就马上计算一下 下标绝对值
            int num = nums[i];
            if(contain_map.count(num)) {
                if(i - contain_map[num] <= k) {
                    return true;
                }
            }
            // 如果没有就记录一下
            contain_map[num] = i;// 记录下标
       }
       return false;
    }
};

在这里插入图片描述

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        int left = 0;
        int right = 0;
        int length = nums.size();
        unordered_set<int> s; // 滑动窗口
        for(int i = 0; i< length ; i++) {
            // 如果在 s 中可以找到重复的元素
            if(i > k) {
                s.erase(nums[i-k-1]);
            }
            // 在维护的 k 窗口中存在 重复元素
            if(s.count(nums[i])) {
                return true;
            }
            // 加入
            s.emplace(nums[i]);
        }
        return false;
    }
};

128. 最长连续序列
在这里插入图片描述

思路
在这里插入图片描述

首先去重
然后按照上面的思路, 检查是否存在 num-1 , 然后往后面循环

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> num_set;
        // 去重
        for(auto num:nums) {
            num_set.insert(num);
        }
        int longStreak = 0;
        
    
        for(auto num: num_set) {
            // 说明这个数字开头
            if(!num_set.count(num-1)) {
                int cur = num;  
                int curStreak = 1;
                // 循环统计 
                while(num_set.count(cur+1)) {
                    cur++;
                    curStreak++;
                }
                longStreak = max(longStreak, curStreak);
            }
        }
        return longStreak;

    }   
};

专题总结

今天,小编希望大家重新写一下上面的题目
下面是我重写的一些收获

赎金信

有关于 unordered_map<char, int> res 键值访问

unordered_map<char, int> res;
res['a'] = 5;  // 插入或更新键'a'的值
int value = res['a'];  // 获取键'a'的值,value现在是5
int default_value = res['b'];  // 键'b'不存在,插入并初始化为0,default_value现在是0

如果访问 res 中没有的按键值, 返回的 为 0
所以在判断键是否存在, 可以使用 res.count(‘a’) 是否为 0 , 也可以使用上面的方式

在这里插入图片描述
这个都是同样类型的, 就不再多说了

快乐数

第一次只能还有这种规律
在这里插入图片描述

219 存在重复元素

在这里插入图片描述
这里使用到 滑动窗口
也就是维护题目中, 长度为 k 的窗口
和队列差不多 , 使用 unordered_set res; // 这里 res 记录的是元素

  • 如果长度超过 k , 左边出列
  • 每一次都检查一下有没有重复元素 s.count(nums[i])
  • 每次在后面添加 元素, s.emplace( nums[i] )
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值