Leetcode3_【哈希表】

哈希表理论基础 了解哈希表的内部实现原理,哈希函数,哈希碰撞,以及常见哈希表的区别,数组,set 和map。  

什么时候想到用哈希法:当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。  这句话很重要,在做哈希表题目都要思考这句话。 

文章讲解:代码随想录

目录

哈希表-数组

242.有效的字母异位词 

383. 赎金信  

49. 字母异位词分组【待补充】

428.找到字符串中所有的字母异位词

【补充】C++ STL

哈希表-set

349. 两个数组的交集 

202. 快乐数 

哈希表 - map

1. 两数之和【语法,回顾】

454.四数相加II 

15. 三数之和 【难】

18. 四数之和  


哈希表-数组

数据量没那么大的时候,用哈希数组比较合适!

242.有效的字母异位词 

建议: 这道题目可以感受到数组用来做哈希表 给我们带来的便利之处。 

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解/视频讲解: 代码随想录

class Solution {
public:
    bool isAnagram(string s, string t) {
        int hash_array[26] = {0}; //ATTENTION:0数组的初始化方式
        for (int i = 0; i < s.size(); i++){
            hash_array[s[i] - 'a']++;
        }
        for(int i = 0; i <t.size(); i++){
            hash_array[t[i] - 'a']--;
        }
        for(int i = 0; i < 26; i++){
            if(hash_array[i] != 0){
                return false;
            }
        }
        return true;
    }
};

改善:

1、前两个for可以写在一起;

2、可以用sort之后比较两个字符串是否相同,49题字母异位词就是这样判断字母异位词的。

383. 赎金信  

建议:本题 和 242.有效的字母异位词 是一个思路 ,算是拓展题 

题目链接:383. 赎金信 - 力扣(LeetCode)

文章讲解:代码随想录

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int hash_arr[26] = {0};
        for(int i = 0; i < magazine.size(); i++){
            hash_arr[magazine[i] - 'a']++;
        }
        for(int i = 0; i < ransomNote.size(); i++){
            hash_arr[ransomNote[i] - 'a']--;
            if(hash_arr[ransomNote[i] - 'a']<0){
                return false;
            }
        }
        return true;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

49. 字母异位词分组

题目链接:49. 字母异位词分组 - 力扣(LeetCode)

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
       vector<vector<string>> res;
       unordered_map<string, vector<string>> map;
       for(int i = 0; i < strs.size(); i++){
         string str = strs[i];
         sort(str.begin(), str.end());
         map[str].push_back(strs[i]);
       }
       for(auto iter: map){
            res.push_back(iter.second);
       }
       return res;
    }
};

428.找到字符串中所有的字母异位词

题目链接:438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

字符串:string.substr(开始索引,截取几个)

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> out;
        int psize = p.size();
        // cout<<psize<<endl;
        for(int i = 0; i < s.size(); i++){
            // cout<<i<<i+psize<<endl;
            string str_temp = s.substr(i,psize);//ATTENTION
            // cout<<str_temp<<endl;
            if(matchString(str_temp, p) == true){
                out.push_back(i);//ATTENTION
            }
        }
        return out;
    }

    bool matchString(string str1, string str2) {
        int array_hash[26] = {0};
        for (int i = 0; i < str1.size(); i++){
            array_hash[str1[i] - 'a']++;
        }
        for (int i = 0; i < str2.size(); i++){
            array_hash[str2[i] - 'a']--;
        }
        for (int i = 0; i < 26; i++){
            if(array_hash[i]!=0){
                return false;
            }
        }
        return true;
    }
};

string.substr(初始索引,索引数量)

vector.push_back(数据) 在vector后面添加数据

滑动窗口的做法:

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

【补充】C++ STL

C++ STL(“Standard Template Library”的缩写,中文译为“标准模板库”)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、列表、集合、链表、队列、栈。

C++ 对模板(Template)支持得很好,STL 就是借助模板把常用的数据结构及其算法都实现了一遍,并且做到了数据结构和算法的分离。例如,vector 的底层为顺序表(数组),list 的底层为双向链表,deque 的底层为循环队列,set 的底层为红黑树,hash_set 的底层为哈希表。

【总结】C++ 基础数据结构 —— STL之集合(set)用法详解_c++ stl set-CSDN博客

【总结】C++ 基础数据结构 —— STL之关联容器(map)用法详解_c++ stl map这个数据结构-CSDN博客

【总结】C++ 数据结构 —— STL之栈(stack)用法详解_c++stl栈-CSDN博客

【总结】C++ 数据结构 —— STL之队列(queue) 用法详解_c++ stl 队列-CSDN博客


【总结】C++ 基础数据结构 —— STL之动态数组(vector)用法详解_stl数组-CSDN博客


给vector赋值
在C++中,给一个std::vector赋值有几种方法。以下是一些常用的方法:

使用初始化列表赋值:

std::vector<int> v = {1, 2, 3, 4, 5};
使用std::vector的赋值操作符:

std::vector<int> v;
v.assign({1, 2, 3, 4, 5}); // 使用初始化列表
 
std::vector<int> v2;
v2.assign(5, 10); // 使用指定的值填充容器
 
std::vector<int> v3;
v3.assign(v.begin(), v.end()); // 使用另一个vector的迭代器范围
使用std::fill算法:

std::vector<int> v(5);
std::fill(v.begin(), v.end(), 10); // 使用std::fill填充容器
使用std::generate算法:

std::vector<int> v(5);
std::generate(v.begin(), v.end(), []() { return rand() % 10; }); // 使用lambda函数生成值
使用std::vector的swap方法与另一个vector交换内容:

std::vector<int> v;
std::vector<int> v2 = {1, 2, 3, 4, 5};
v.swap(v2); // 交换v和v2的内容
使用std::copy算法从另一个容器复制内容:

std::vector<int> v(5);
std::vector<int> v2 = {1, 2, 3, 4, 5};
std::copy(v2.begin(), v2.end(), v.begin()); // 从v2到v
这些是给std::vector赋值的一些常用方法。选择哪种方法取决于具体的需求和场景。

哈希表-set

349. 两个数组的交集 

建议:本题就开始考虑 什么时候用set 什么时候用数组,本题其实是使用set的好题,但是后来力扣改了题目描述和 测试用例,添加了 0 <= nums1[i], nums2[i] <= 1000 条件,所以使用数组也可以了,不过建议忽略这个条件。 尝试去使用set。 

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解/视频讲解:代码随想录

unordered_set 不需要去重(与mult_set不同)

unordered_set的定义:   unordered_set<int> set2;    

unordered_set的定义+初始化:unordered_set<int> set1(nums1.begin(),nums1.end());
 

set.find(元素):set容器中查找值为x的元素O(log n):若存在,返回一个迭代器,指向键值x;若不存在,返回一个迭代器,指向set.end()。

set.insert(元素):向集合中插入元素

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> set1(nums1.begin(),nums1.end());//ATTENTION
        unordered_set<int> set2;
        for (int num:nums2){//ATTENTION
            if(set1.find(num) != set1.end()){//找到了//ATTENTION
                set2.insert(num);
            }
        }
        return vector<int> (set2.begin(),set2.end());//ATTENTION
    }
};

202. 快乐数 

建议:这道题目也是set的应用,其实和上一题差不多,就是套在快乐数一个壳子 

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解:代码随想录

难点:1、说到sum可以无限循环意味着sum如果重复出现会导致无线循环,因此可以用set

来存放,看看能不能找到重复的;2、数字的单位操作

class Solution {
public:
    bool isHappy(int n) {
        unordered_set<int> set;
        while(1){
            int sum = getSum(n);
            if(sum == 1){
                return true;
            }
            if(set.find(sum)!=set.end()){
                return false;
            }else{
                set.insert(sum);
            }
            n = sum;
        }
    }

    int getSum(int n){
        int sum = 0;
        while(n){
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
};

3. 无重复字符的最长子串

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_set<char> set;
        int left =0;
        int maxStr = 0;
        for(int i = 0; i < s.size(); i++){
            while(set.find(s[i]) != set.end()){//如果找到重复的了
                set.erase(s[left]);//从左边开始擦,擦到不包括本s[i],后面再把它插进来
                left ++;//left的作用就是把从左边开始擦
            }
            //maxStr = max(maxStr,i-left+1);//或者这样也可以
            set.insert(s[i]);
            maxStr = maxStr > set.size() ? maxStr : set.size();
        }
        return maxStr;
    }
};

哈希表 - map

map : key value

map 的定义:unordered_map<int,int> map1;

map.find(元素):返回指向该元素的迭代器

插入:

直接map[键]=键值

map.insert(e):插入一个value_type类型的值
map.insert(beg, end):beg和end标记的是迭代器的开始和结束
map.insert(iter, e)

1. 两数之和【语法,回顾】

建议:本题虽然是 力扣第一题,但是还是挺难的,也是 代码随想录中 数组,set之后,使用map解决哈希问题的第一题。 先看视频讲解,然后尝试自己写代码,在看文章讲解,加深印象。 

题目链接:1. 两数之和 - 力扣(LeetCode)

文章讲解/视频讲解:代码随想录

如果尝试用hash-set做的话,是不行的,因为不能获取到匹配的索引

//errrrrrrrrror
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_set<int> set1;
        vector<int> res;
        for(int i = 0; i < nums.size(); i++){
            if(set1.find(target-nums[i])!=set1.end()){
                res.push_back(nums[i]);
                // set1.find(target-nums[i]);
                res.push_back(...);//希望获取索引,但是因为是集合所以不行
                return res;
            }else{
                set1.insert(nums[i]);
            }   
        }
        return res;
    }
};

如果是map就可以

class Solution {public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> map1;
        for(int i = 0; i < nums.size(); i++){
            if(map1.find(target-nums[i])!=map1.end()){
                return {map1.find(target-nums[i])->second,i};
            }else{
                map1.insert(pair<int,int>(nums[i],i));//内容,下标
                //或者:map1[nums[i]]=i;
            }   
        }
        return {};
    }
};

454.四数相加II 

建议:本题是 使用map 巧妙解决的问题,好好体会一下 哈希法 如何提高程序执行效率,降低时间复杂度,当然使用哈希法 会提高空间复杂度,但一般来说我们都是舍空间 换时间, 工业开发也是这样。

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解/视频讲解:代码随想录

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int> map_12;//sumvalue & nums
   
        int count = 0;
        for(int i:nums1){
            for(int j:nums2){
                map_12[i+j]++;//ATTENTION
            }
        }
        // DONT DO THAT
        // for(int i = 0; i<nums1.size(); i++){
        //     for(int j = 0; j<nums2.size(); j++){
        //         count12++;
        //         map_12.insert(pair<int><int(nums1[i]+nums[j],count12)>); //不好统计count12
        //     }
        // }
        for(int p:nums3){
            for(int k:nums4){
                // map_34[p+k]++;
                if(map_12.find(0-(p+k))!=map_12.end()){
                    count = count + map_12[0-(p+k)];
                }
            }
        }
        return count;

    }
};

15. 三数之和 【难,双指针,去重】

建议:本题虽然和 两数之和 很像,也能用哈希法,但用哈希法会很麻烦,双指针法才是正解,可以先看视频理解一下 双指针法的思路,文章中讲解的,没问题 哈希法很麻烦。 

题目链接:

文章讲解/视频讲解:代码随想录

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());
        for(int i = 0 ; i < nums.size(); i++){
            if(nums[i] > 0) return res;//res中已经存了好几组满足题意的了,可以返回了
            //去重i
            if(i > 0 && nums[i - 1] == nums[i]) continue;
            int left = i + 1;
            int right = nums.size() - 1;
            while(left < right){//因为不能重叠所以<
                if(nums[i] + nums[left] + nums[right] > 0) right--;
                else if(nums[i] + nums[left] + nums[right] < 0) left++;
                else{//找到满足条件的了
                    res.push_back(vector<int>{nums[i] , nums[left] , nums[right]});
                    //去重left、right
                    while(right > left && nums[right] == nums[right - 1]) right--;
                    while(right > left && nums[left] == nums[left + 1]) left++;
                    //找到答案后,收缩
                    right--;
                    left++;
                }
            }
        }
        return res;
        
    }
};

18. 四数之和  

建议: 要比较一下,本题和 454.四数相加II 的区别,为什么 454.四数相加II 会简单很多,这个想明白了,对本题理解就深刻了。 本题 思路整体和 三数之和一样的,都是双指针,但写的时候 有很多小细节,需要注意,建议先看视频。 

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解/视频讲解:代码随想录

128. 最长连续序列

如果题目没有要求时间复杂度

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int legth = 1;
        int res = 1;
        if(nums.size() < 2) return nums.size();
        for(int i = 1; i < nums.size(); i++){
            if(nums[i] - nums[i-1] == 1){
                legth ++;
                res = res > legth ? res : legth;
            }else if(nums[i] == nums[i-1]){}
            else{
                legth = 1;
            }
        }
        return res;
    }
};

但是题目要去时间复杂度是O(n),需要用到哈希表

满射问题/判断是否具有相同规律【hush表】

205. 同构字符串

class Solution {
public:
    bool isIsomorphic(string s, string t) {
        if(s.size() != t.size()){
            return false;
        }
        unordered_map<char,char> maps2t;
        unordered_map<char,char> mapt2s;
        int n = s.size();
        for(int i = 0; i < n; i ++){
            if(maps2t.find(s[i]) == maps2t.end()){
                maps2t[s[i]] = t[i];
            }
            if(mapt2s.find(t[i]) == mapt2s.end()){
                mapt2s[t[i]] = s[i];
            }
            if(maps2t.find(s[i]) != maps2t.end() && maps2t[s[i]] != t[i] ||
            mapt2s.find(t[i]) == mapt2s.end() && mapt2s[t[i]] != s[i]){
                return false;
            }
        }
        return true;
    }
};

290. 单词规律

class Solution {
public:
    bool wordPattern(string pattern, string s) {
        unordered_map<char,string> maps2t;
        unordered_map<string,char> mapt2s;
        vector<string> vec = st2vec(s);
        if(pattern.size() != vec.size()) return false;
        int n = vec.size();
        for(int i = 0; i < n; i ++){
            if(maps2t.find(pattern[i]) == maps2t.end()){
                maps2t[pattern[i]] = vec[i];
            }
            if(mapt2s.find(vec[i]) == mapt2s.end()){
                mapt2s[vec[i]] = pattern[i];
            }
            if(maps2t.find(pattern[i]) != maps2t.end() && maps2t[pattern[i]] != vec[i] ||
            mapt2s.find(vec[i]) != mapt2s.end() && mapt2s[vec[i]] != pattern[i]){
                return false;
            }
        }
        return true;
    }
private:
    vector<string> st2vec(string str){
        vector<string> res;
        string word = "";
        for(char i : str){ 
            if(i == ' '){
                res.push_back(word);
                word= "";
            }else{
                word += i;
            }
            
        }
        res.push_back(word);
        return res;
    }
};

219. 存在重复元素 II

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_map<int, vector<int>> map;
        for(int i = 0; i < nums.size(); i++){
            map[nums[i]].push_back(i);
        }
        for(auto p : map){
            vector<int> vec = p.second;
            if(vec.size() > 1){
                return judge(vec, k);
            }
        }
        return false;
    }
    bool judge(vector<int>& vec, int k){
        for(int i = 1; i < vec.size(); i++){
            if(vec[i] - vec[i - 1] <= k) return true;
        }
        return false;
    }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值