【代码随想录】哈希表——“字母异位词”相关题目

哈希表能解决什么问题呢,一般哈希表都是用来快速判断一个元素是否出现集合里。

要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。

但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找

常见的三种哈希结构

当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。

  • 数组
  • set (集合)
  • map(映射)

 C++中,set 和 map 分别提供以下三种数据结构,其底层实现以及优劣如下表所示

集合(set)底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率
std::set红黑树有序O(log n)O(log n)
std::multiset红黑树有序O(logn)O(logn)
std::unordered_set哈希表无序O(1)O(1)

红黑树是一种平衡二叉搜索树所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。 

映射(map)底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率
std::map红黑树key有序key不可重复key不可修改O(logn)O(logn)
std::multimap红黑树key有序key可重复key不可修改O(log n)O(log n)
std::unordered_map哈希表key无序key不可重复key不可修改O(1)O(1)

242. 有效的字母异位词

先判断两个字符串的长度是否相等,如果不相等,直接返回false

否则,两个字符串一起操作,同一个哈希表,一个++,一个减减

最后使用迭代器看看哈希表里面的元素是否全为0,如果存在不为0的,返回false;

否则,返回true。

class Solution {
public:
    bool isAnagram(string s, string t) {
        int m=s.size();
        int n=t.size();
        if(m!=n) return false;
        unordered_map<char,int> map;
        for(int i=0;i<m;i++){//前面判断了如果两者长度不一样,肯定会return false。这里的m肯定等于n,两个字符串一起操作即可
            map[s[i]]++;
            map[t[i]]--;
        }
        //哈希表操作:迭代器
        for(unordered_map<char,int>::iterator it=map.begin();it!=map.end();it++){
            if(it->second!=0) return false;
        }
        return true;
    }
};

383. 赎金信

方法一:哈希表+迭代器

观察发现,当前者字符串的长度大于后者字符串长度时,肯定不可能构成题目要求的。

0~m-1长度,利用哈希表前者++,后者减减

m~n-1长度,利用哈希表,计算后者字符串的值,也即是减减操作

迭代器判断,如果发现有大于0的,表明前者字符串还差字符,构不成,直接return false

否则,返回true

 for(unordered_map<char,int>::iterator it=map.begin();it!=map.end();it++){
            if(it->second>0) return false;
        }
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int m=ransomNote.size();
        int n=magazine.size();
        if(m>n) return false;
        unordered_map<char,int> map;
        for(int i=0;i<m;i++){
            map[ransomNote[i]]++;
            map[magazine[i]]--;
        }
        for(int j=m;j<n;j++){
            map[magazine[j]]--;
        }
        for(unordered_map<char,int>::iterator it=map.begin();it!=map.end();it++){
            if(it->second>0) return false;
        }
        return true;


    }
};

方法二:用数组做哈希

定义一个数组叫做record用来上记录字符串里字符出现的次数,初始化伪0

在遍历后者字符串的时候,只需要将 magazine[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。

接着在遍历前者字符串的时候,只需要将 ransomNote[i] - ‘a’ 所在的元素做-1 操作即可,接着判断其是否小于0(表明后者字符串magazine中没有前者字符串中的字符),直接返回false

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int record[26]={0};//记录两个字符串中字符的相对值,初始化为0
        for(int i=0;i<magazine.size();i++){
            record[magazine[i]-'a']+=1;
        }
        for(int j=0;j<ransomNote.size();j++){
            record[ransomNote[j]-'a']-=1;
            if(record[ransomNote[j]-'a']<0) return false;
        }
        return true;
    }
};

 49. 字母异位词分组

哈希表+迭代器

定义哈希表,第一个元素存放的是经过排序的单词,第二个元素存放的是字母异位词组(原来的词)

遍历字符串数组,依次取单词暂存为temp,排序每个单词,然后将排序好的单词和原来的单词作为一组放一起(也即是map[temp].push_back(strs[i]);)。

迭代器遍历哈希表 for(auto it=map.begin();it!=map.end();it++),auto比unordered_map<string,vector<string>>::iterator省时间。依次将it->second push—back进res

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        vector<vector<string>>res;
        int n=strs.size();
        if(n==0||n==1) return {strs};
        unordered_map<string,vector<string>> map;//map中第一个存放的是经过排序的单词,第二个存放的是字母异位词组(原来的词)
        for(int i=0;i<n;i++){
            string temp=strs[i];//依次取单词
            sort(temp.begin(),temp.end());//将依次取出来的单词排序
            map[temp].push_back(strs[i]);//map.first是排序好的单词,map.second是原单词
        }
        //使用迭代器
        for(auto it=map.begin();it!=map.end();it++){
            res.push_back(it->second);
        }
        return res;


    }
};

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

由于题目都限制了数组的大小,用数组做哈希

vector<int> record_s(26,0),record_p(26,0);

不需要记住字符的ASCII,只要求出一个相对数值就可以了。也就是 record_s[s[i]-'a']++;

先比较前m个,判断两个vector是否相等。接着窗口移动(for(;i<s.size();i++){//窗口移动)

将排出窗口的元素对应的数组-1,新进窗口的元素对应数组+1,判断两个vector是否相等(相等则把左边界放进res中)

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> res;
        if(s.size()<p.size()) return res;
        vector<int> record_s(26,0),record_p(26,0);
        int i=0,m=p.size();
        for(;i<m;i++){//比较前M个
            record_s[s[i]-'a']++;
            record_p[p[i]-'a']++;
        }
        if(record_p==record_s) res.push_back(0);//vector可以直接比较大小

        for(;i<s.size();i++){//窗口移动
            record_s[s[i-m]-'a']--;//将移出窗口的元素所对应的数组减1
            record_s[s[i]-'a']++;
            if(record_p==record_s) res.push_back(i-m+1);
        }
        return res;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值