哈希表——从LeetCode实战中总结常用套路

本来想总结一下套路,结果写完发现这一篇博客成了水题合集

不过还是有很多思考与启发的!

目录

LeetCode1365.有多少小于当前的数字

LeetCode771.宝石与石头

LeetCode面试题16.02.单词频率

LeetCode1207.独一无二的出现次数

LeetCode500.键盘行

LeetCode349.两个数组的交集

LeetCode面试题03.数组中重复的数字

LeetCode1208.尽可能使字符串相等


哈希表(Hash Table,也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做哈希函数,存放记录的数组称做哈希表。

一个通俗的例子是,为了查找电话簿中某人的号码,可以创建一个按照人名首字母顺序排列的表(即建立人名 xxx 到首字母 F(x)F(x)F(x) 的一个函数关系),在首字母为 WWW 的表中查找 “王” 姓的电话号码,显然比直接查找就要快得多。这里使用人名作为关键字,“取首字母” 是这个例子中哈希函数的函数法则 F()F()F(),存放首字母的表对应哈希表。关键字和函数法则理论上可以任意确定。

哈希表是使用 O(1)O(1)O(1) 时间进行数据的插入删除和查找,但是哈希表不保证表中数据的有序性,这样在哈希表中查找最大数据或者最小数据的时间是 O(N)O(N)O(N) 实现。

LeetCode387.字符串中的第一个唯一字符

class Solution {
public:
    int firstUniqChar(string s) {
        unordered_map<char,int> m;
        for (auto elem : s) 
            m[elem]++;
        for (int i = 0; i < s.size(); i++) {
            if (m[s[i]] == 1)
                return i;
        }
        return -1;
    }
};

LeetCode1365.有多少小于当前的数字

方法一:暴力求解

空间复杂度居然还可以,击败了所有人,我也是很疑惑……

class Solution {
public:
    vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
        vector<int> a;
        for(int i=0;i<nums.size();i++){;
            int b = 0;
            for(int j=0;j<nums.size();j++){
                if(nums[j]<nums[i])
                    b++;
            }
            a.push_back(b);
        }
        return a;
    }
};

还能更好吗? 当然可以用哈希表做,但是复杂度比这个高,所以这题对学习哈希表来说没什么意义……

LeetCode771.宝石与石头

用两个for嵌套循环暴力法,居然时间复杂度最高,我很迷啊

class Solution {
public:
    int numJewelsInStones(string J, string S) {
        int sum = 0;
        for(int i=0;i<J.size();i++){
            for(int j=0;j<S.size();j++){
                if(J[i]==S[j])
                    sum++;
            }
        }
        return sum;
    }
};

用木桶排序的思想,将两个嵌套的for循环变成两个独立的for循环

时空复杂度反而更低,醉了

class Solution {
public:
    int numJewelsInStones(string J, string S) {
        int sum = 0;
        set<char> jew; 
        for(auto i:J)
            jew.insert(i);//记录宝石类型
        for(auto s:S)
            if(jew.count(s))
                sum++;//若拥有的石头里有宝石,答案加一
        return sum;
    }
};

LeetCode面试题16.02.单词频率

当然可以用结构体做,但肯定会超时!感受一下恐怖的测试样例一角:

class WordsFrequency {
public:
    struct b{
        string s;
        int num = 0;
    }b[100001];
    int size;
    WordsFrequency(vector<string>& book) {
        size = book.size();
        for(int i=0;i<book.size();i++){
            b[i].s = book[i];
            b[i].num++;
        }
    }
    
    int get(string word) {
        int n = 0;
        for(int i=0;i<size;i++){
            if(b[i].s == word)
                n+=b[i].num;
        }
        return n;
    }
};

/**
 * Your WordsFrequency object will be instantiated and called as such:
 * WordsFrequency* obj = new WordsFrequency(book);
 * int param_1 = obj->get(word);
 */

想不超时,改成哈希表解决即可!

使用map,内存可以击败100%哈哈

class WordsFrequency {
public:
    map<string,int> m;
    WordsFrequency(vector<string>& book) {
        for(int i=0;i<book.size();i++){
            m[book[i]]++;
        }
    }
  
    int get(string word) {
        if(m.find(word)!=m.end())
            return m[word];
        return 0;
    }
};

/**
 * Your WordsFrequency object will be instantiated and called as such:
 * WordsFrequency* obj = new WordsFrequency(book);
 * int param_1 = obj->get(word);
 */

 也可以换用可重复的muliset:

class WordsFrequency {
public:

    multiset<string> s;

    WordsFrequency(vector<string>& book) {
        int len = book.size();
        for(int i = 0 ; i < len; i++){
            s.insert(book.at(i));
        }
    }
    
    int get(string word) {
        return s.count(word);
    }
};

/**
 * Your WordsFrequency object will be instantiated and called as such:
 * WordsFrequency* obj = new WordsFrequency(book);
 * int param_1 = obj->get(word);
 */

时空复杂度差不多……

LeetCode1207.独一无二的出现次数

套路:以后碰到判断时候用重复的元素的题目,只要将其插入到set中,set会自动去重,再比较去重前后size大小是否变化就可以快速判断是否有重复元素!

class Solution {
public:
    bool uniqueOccurrences(vector<int>& arr) {
        map<int,int> m;
        for(int i=0;i<arr.size();i++){
            m[arr[i]]++;
        }
        set<int>times;
        for(auto p:m)
            times.insert(p.second);
        return times.size()==m.size();
    }
};

注意这里map不需要排序,理论上使用unordered_map比使用map的时间复杂度要低很多!

但是实际上map比unorder_map快不少,这是为啥……

LeetCode500.键盘行

用正则表达式空间复杂度会小一些,也会简洁不少;

但是搞个set套餐暴力模拟,非常快,可以干掉everybody

就是把键盘上这么多字母输入进去有点麻烦,可能是我太懒了哈哈

class Solution {
public:
    vector<string> findWords(vector<string>& words) {
        char a1[20] = {'q','w','e','r','t','y','u','i','o','p','Q','W','E','R','T','Y','U','I','O','P'};
        char a2[18] = {'a','s','d','f','g','h','j','k','l','A','S','D','F','G','H','J','K','L'};
        char a3[14] = {'z','x','c','v','b','n','m','Z','X','C','V','B','N','M'};
        set<char> s1(a1,a1+20);
        set<char> s2(a2,a2+18);
        set<char> s3(a3,a3+14);
        vector<string> findword;
        for(int i=0;i<words.size();i++){
            int c1=0,c2=0,c3=0;
            for(int j=0;j<words[i].size();j++){
                if(s1.find(words[i][j])!=s1.end())
                    c1++;
                if(s2.find(words[i][j])!=s2.end())
                    c2++;
                if(s3.find(words[i][j])!=s3.end())
                    c3++;
            }
            //如果三个数中只有一个为0,则符合条件
            if((c1&&!c2&&!c3)||(!c1&&!c2&&c3)||(!c1&&c2&&!c3))
                findword.push_back(words[i]);
        }
        return findword;
    }
};

LeetCode349.两个数组的交集

用两个set,去重查找

用木桶可能会快一些,但是题目没说大数的范围,可能有坑……

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        set<int> s1(nums1.begin(),nums1.end());
        set<int> s2(nums2.begin(),nums2.end());
        vector<int> a;
        for(set<int>::iterator it = s1.begin();it!=s1.end();it++){
            if(s2.find(*it)!=s2.end())
                a.push_back(*it);
        }
        return a;
    }
};

LeetCode面试题03.数组中重复的数字

用木桶法可以用空间换时间,空间上可以击败everybody

当然可以用哈希法,用时间换空间

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int box[nums.size()]={0};
        for(int i=0;i<nums.size();i++){
            box[nums[i]]++;
            if(box[nums[i]]>1)
                return nums[i];
        }
        return 0;
    }
};

LeetCode1208.尽可能使字符串相等

这题用给的FindElements函数,没有返回值,不好直接构造递归形式的函数;

所以将其拆出来添加一个参数,这种写法可以省下不少麻烦,这一点也是我之前没有认真思考过的!

所以思维要尽量打开啊

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class FindElements {
public:
    FindElements(TreeNode* root) {
        root->val = 0;
        fun(root,0);
    }
    bool find(int target) {
        return s.find(target)!=s.end();
    }
    void fun(TreeNode* root,int value){
        if(root==NULL)
            return;
        s.insert(value);
        root->val = value;
        fun(root->left,2*value+1);
        fun(root->right,2*value+2);
    }
private:
    set<int> s;

};

/**
 * Your FindElements object will be instantiated and called as such:
 * FindElements* obj = new FindElements(root);
 * bool param_1 = obj->find(target);
 */

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉迷单车的追风少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值