LeetCode-哈希表

1.题号217. 存在重复元素

遍历,放入散列集中,如果散列集中已经存在,返回true

bool containsDuplicate(vector<int>& nums) {
        unordered_set<int> s;
        for(int i:nums){
            if(s.find(i)!=s.end()){
                return true;
            }
            s.insert(i);
        }
        return false;
    }

2.题号219. 存在重复元素 II

要用nums[i]做key,i做value
找到了并符合条件,返回true
因为从左向右找,当前索引一定是大于哈希表中的,所以直接用i-j判断
需要更新最近的重复元素索引,避免{1,0,1,1} 1,这种情况

bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_map<int,int> hash;
        int n = nums.size();
        for(int i=0;i<n;i++){
            unordered_map<int,int>::iterator it=hash.find(nums[i]);
            if(it!=hash.end()){
                if((i-it->second)<=k){  找到了,并且符合条件
                    return true;
                }else{
                    hash[nums[i]]=i;	找到了,但超出了范围,需要更新索引为当前
                }
            }
            hash.insert({nums[i],i});	放入哈希表
        }
        return false;
    }

3.题号904. 水果成篮

滑动窗口与哈希表的完美结合
用哈希表记录每种水果的个数,很方便直接用fruits[tree[i]]++就可以操作
当水果数量超过两种,就l右移,但不是平移,当对应个数为0时要删除水果种类(如果不删除,即使数量为0也会让size+1),直到篮中水果个数小于二,再继续扩张

int getCount(unordered_map<int, int>& mp) {
        int ret = 0;
        for (auto it : mp) {
            ret += it.second;
        }
        return ret;
    }
    int totalFruit(vector<int>& tree) {
        unordered_map<int, int> fruits;
        int ans = 0,l = 0;
        for(int i=0;i<tree.size();i++){
            fruits[tree[i]]++;
            while(l<tree.size() && fruits.size()>2){
                fruits[tree[l]]--;
                if(fruits[tree[l]]==0){
                    fruits.erase(tree[l]);
                }
                l++;
            }
            ans=max(ans,getCount(fruits));
        } 
        return ans;
    }

4.题号1365. 有多少小于当前数字的数字

用bucket记录每个数字出现次数,索引代表值,元素代表个数
再遍历一遍bucket,让除索引0之外的都+=他之前元素,这样bucket[nums[i]-1]
装的就是,小于他的数总和,避免了for循环的嵌套,妙

vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
        vector<int> bucket(101);
        vector<int> ans;
        for(int i=0;i<nums.size();i++){
            bucket[nums[i]]++;
        }
        for(int i=1;i<101;i++){		妙啊
            bucket[i]+=bucket[i-1];
        }
        for(int i=0;i<nums.size();i++){
            ans.push_back(nums[i]==0 ? 0:bucket[nums[i]-1]);
        }
        return ans;
    }

5.题号1160. 拼写单词

用哈希表记录了单词的每个字符数量,比较单词每个字符数量与可用的比较。
用数组记录,遇到相同count–,也可以,但每次都要回复可用字符数组到初始状态

int countCharacters(vector<string>& words, string chars) {
       unordered_map<char,int> hash;
       int length=0;
       for(char c:chars){
           hash[c]++;
       } 
       for(string word:words){
           unordered_map<char,int> word_hash;
           for(char c:word){
                word_hash[c]++;
           }
           bool flag=true;
           for(char c:word){
                if(word_hash[c]>hash[c]){
                    flag=false;
                    break;
                }
           }
           if(flag){
               length+=word.size();
           }
       }
       return length;
    }

6.题号884. 两句话中的不常见单词

学会了用istringstream分隔字符串
妙在把两个字符串+“ ”连接在一起,就可以只遍历一次
把每个单词用哈希表计数,最后遍历哈希表把次数为1的单词添加到结果数组

vector<string> uncommonFromSentences(string A, string B) {
        unordered_map<string,int> map;
        vector<string> ans;
        A = A+" "+B;
        istringstream ss(A);
        string word;
        while(ss>>word){
            map[word]++;
        }
        for(auto it:map){
            if(it.second==1){
                ans.push_back(it.first);
            }
        }
        return ans;
    }

7. 题号350. 两个数组的交集 II

记录第一个数组每个数字出现次数,遍历第二个数组,相同次数–,加到答案数组,到次数为0时,证明这个数字用完了,删除数字,最后返回答案数组即可

vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        unordered_map<int,int> map;
        vector<int> ans;
        for(int i:nums1){
            map[i]++;
        }
        for(int i:nums2){
            if(map.count(i)){
                ans.push_back(i);
                map[i]--;
            }
            if(map[i]==0){
                map.erase(i);
            }
        }
        return ans;
    }

8. 题号599. 两个列表的最小索引总和

题意是有索引和相同的都加入到数组中
用哈希储存索引,与当前元素索引相加

vector<string> findRestaurant(vector<string>& list1, vector<string>& list2) {
        unordered_map<string,int> map;
        vector<string> ans; 
        int n=list1.size(),m=list2.size(),min=INT_MAX;
        for(int i=0;i<n;i++){
            map[list1[i]]=i;
        }
        for(int i=0;i<m;i++){
            int sum=INT_MAX;
            auto it=map.find(list2[i]);
            if(it!=map.end()){
                sum=i+it->second;
            }   
            if(sum==min){	这个要先判断,写在后面,会重复插入
                ans.push_back(list2[i]);
            }                               
            if(sum<min){
                min=sum;
                ans.clear();
                ans.push_back(list2[i]);
            }
        }
        return ans;
    }

9. 1711. 大餐计数

高级版两数之和,由于每个数小于等于2的20次方,所以两数之和小于等于2的21磁钢,所以我们只需要判断有没有另一个数存在,让两数之和等于2的0到21次方
如果ans不想用long long可以写为
ans = (ans + map[dif])% mod;

int countPairs(vector<int>& deliciousness) {
        unordered_map<int,int> map;
        long long ans = 0;
        int mod = 1e9 + 7;
        for(int i = 0; i < deliciousness.size(); i++){
            for(int j = 0; j <= 21; j++){
                int dif = (1 << j) - deliciousness[i];
                if(dif < 0) continue;
                if(map.count(dif)){
                    ans += map[dif];
                }
            }
            map[deliciousness[i]]++;
        }
        return ans % mod;
    }

10. 697. 数组的度

最短子数组,度还一样,意味着就是,长度为最多的元素第一次出现到最后一次出现的距离
用一个哈希表,键为值,value用pair记录,first为出现次数,second为第一次出现索引,用于记录最短长度
当出现次数与最多次持平时,记录二者最短值,当出现次数大于最大值时,更新答案为他的长度

int findShortestSubArray(vector<int>& nums) {
        unordered_map<int,pair<int,int>> map;
        int maxCount = 0,minLength = 1;
        for(int i = 0; i < nums.size(); i++){
            map[nums[i]].first++;
            if(map[nums[i]].first == 1){
                map[nums[i]].second = i;
            }
            if(maxCount == map[nums[i]].first){
                minLength = min(minLength,i-map[nums[i]].second+1);
            }
            else if(maxCount < map[nums[i]].first){
                maxCount = map[nums[i]].first;
                minLength = i - map[nums[i]].second + 1;
            }
        }
        return minLength;
    }

11. 面试题 17.11. 单词距离

方法一:
用哈希表记录下单词所有索引位置,与另一个所有单词索引做差取最小值
因为题目要多次比较,所以用哈希表比较好

int findClosest(vector<string>& words, string word1, string word2) {
        unordered_map<string,vector<int>> map;
        for(int i = 0; i < words.size(); i++){
            if(!map.count(words[i])){
                map[words[i]] = {};
            }
            map[words[i]].push_back(i);
        }
        int ans = INT_MAX;
        for(int i = 0; i < map[word1].size(); i++){
            for(int j = 0; j < map[word2].size(); j++){
                int dif = abs(map[word1][i] - map[word2][j]);
                ans = min(ans,dif);
            }
        }
        return ans;
    }

方法二:
只需比较相邻的两个要求单词索引,最小值一定出现在其中
只需要查找一次时,这种方法更好

int findClosest(vector<string>& words, string word1, string word2) {
        int index1 = -1,index2 = -1,ans = INT_MAX;
        for(int i = 0; i < words.size(); i++){
            if(words[i] == word1){
                index1 = i;
            }
            else if(words[i] == word2){
                index2 = i;
            }
            if(index1 != -1 && index2 != -1){
                ans = min(ans,abs(index1 - index2));
            }
        }
        return ans;
    }

12. 剑指 Offer 03. 数组中重复的数字

方法一:set
方法二:
数组元素0到n-1,没有重复的时候正好一个萝卜一个坑
正常归位操作,当自己的坑位已经被相同的萝卜占了时,说明他是重复元素,返回即可
当萝卜恰好在自己的坑时,不会返回,因为只有萝卜不在自己坑时才进行判断

int findRepeatNumber(vector<int>& nums) {
        for(int i = 0; i < nums.size() ;i++){
            while(nums[i] != i){
                if(nums[nums[i]] == nums[i]){
                    return nums[i];
                }
                swap(nums[i],nums[nums[i]]);
            }
        }
        return -1;
    }

收获与体会

  1. 要查找的值作为键,因为是根据键查找
  2. 可以用哈希表记录数组中每个元素出现的个数
  3. 只要循环中有i++,while条件就不要忘了加 i<n 的类似条件
  4. 不需要记录次数用set,需要记录次数用map
  5. min要更新为INT_MAX
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值