哈希表

47 阅读5分钟

总结

  1. 有两种哈希结构set,map,其中又分为set,multiset,unordered_set,前两者底层实现是红黑树,因此是有序的,多重表的键值可以重复;而无序集合查询效率更高。
  2. 常用函数,insert(),push_back(),pop(),begin(),end(),empty();
  3. 集合的快速初始化,可以快速联动,例如unordered_set<int>nums_in_1(nums1.begin(),nums1.end());
  4. map的使用,map.insert(pair<int,int>(key,val))
  5. 集合的遍历:for(auto val : nums1){}
  6. 统计数字或者字母可以直接使用
  7. abcd都是int大整数,想要结果正常化可以先加之后再强制转化(long long )a+b+c+d

01字母异位词

Problem: 242. 有效的字母异位词

解题方法

简单题,使用数组就可以做到

Code

#include <unordered_set>
class Solution {
public:
    bool isAnagram(string s, string t) {
        int charNum[26] = {0};
        int i;
        for(i = 0;i<s.length();i++){
            charNum[s[i]-'a']++;  
        }
        for(i = 0;i<t.length();i++){
            charNum[t[i]-'a']--;  
        }

        for(i = 0;i<26;i++){
            if(charNum[i]!=0){
                return false;
            }
        }
        return true;
        

    }
};

02数组交集

Problem: 349. 两个数组的交集

[TOC]

解题方法

刚开始想到的是用一个集合,用其中一个vector,后来发现vector其中去重搞不定。

复杂度

时间复杂度:

两个向量长度为n,m; 因为每个元素都得遍历,所以复杂度为n+m,查找元素时候,由于是无序表所以复杂度为m

空间复杂度:

n需要申请额外的一个集合的空间

Code

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> nums_in_1(nums1.begin(),nums1.end());//数据结构之间的快速联动
        unordered_set<int> result;

        for (int val : nums2) {
            if (nums_in_1.find(val)!=nums_in_1.end()) {
                result.insert(val);
            }
        }
        return vector<int>(result.begin(),result.end());
    }
};

03快乐数

Problem: 202. 快乐数

[TOC]

思路

想到了通过哈希表检测循环,如果为1则true,如果为其他的则false.通过这题学到了如何遍历一个数的各个数字,while(n){ c=n%10;n=n/10;}

复杂度

时间复杂度:

n

空间复杂度:

添加空间复杂度, 示例: O(n)O(n)

Code

class Solution {
public:
    bool isHappy(int n) { 
        unordered_set<int> sqare_sum;
        sqare_sum.insert(n); 
        n = calculate_sqare_sum(n);
        while(sqare_sum.find(n)== sqare_sum.end()){
            sqare_sum.insert(n);
            n = calculate_sqare_sum(n);
        }
        if(n == 1){
            return true;
        }
        else{
            return false;
        }     
    }
    int calculate_sqare_sum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n=n/10;
        }
        return sum;
    }
};

04两数之和

Problem: 1. 两数之和

[TOC]

思路

看到这一题没有想到用map解题,只想到了复杂的判断循环,导致越写越复杂。 凡是需要用到去重的,都可以使用哈希结构。

解题方法

描述你的解题方法

复杂度

时间复杂度:

哈希表查找效率是logn,此处使用的是无序map所以结果为O(1),每个元素都需要遍历,最后复杂度是n

空间复杂度:

添加空间复杂度, 示例: O(n)O(n)

Code

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> index_map;
        for(int i = 0;i < nums.size();i++){
            unordered_map<int, int>::iterator it = index_map.find(target-nums[i]);
            if(it!=index_map.end()){
                //如果找到了对应的值
                return {it->second,i};
            }
            else{
                index_map.insert(pair<int,int>(nums[i],i));//把值和索引存进去
            }
        }

        return {};

                
    }
};

05四数相加等于0

Problem: 454. 四数相加 II

[TOC]

思路

  1. 刚看到这题想到的是用map记录每个数字出现的次数,然后三个for循环得出0-i-j-k之后在map里查找,但是时间复杂度太高以及unordered_map导致超时。两个for循环把复杂度降到了n2n^2
  2. 对于统计数字出现次数的map不够熟悉,记录如下:
unordered_map<int,int> ab_map;
for (int a: nums1){
    for(int b: nums2){
        ab_map[a+b]++;//先插入a+b默认值为0,之后++实现+1的操作。
    }
}

Code

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3,
                     vector<int>& nums4) {
        unordered_map<int, int> cd_map;//很关键的无序map
        for (int c : nums1) {
            for(int d :nums2){
                cd_map[c + d]++;//
            }
        }
        int num_tuple = 0;
        int need;

        for(int a:nums3){
            for(int b: nums4){
                need=0- a-b;

                if(cd_map.find(need)!=cd_map.end()){
                    num_tuple+=cd_map[need];
                }
            }
        }
        return num_tuple;
    }
};

06赎金信

思路

这题想的是如何优化达到最小的时间复杂度,如果用数组存杂志,那么复杂度为M+2R如果用数组存赎金信那么复杂度为R+2M+26,在小样本下二者差不多

Code

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        // if (ransomNote.size() > magazine.size()) return false;
        int record[26] = {0};
        for (auto s : magazine) {
            record[s - 'a']++;
        }
        for (auto s : ransomNote) {
            record[s - 'a']--;
            if (record[s - 'a'] < 0) {
                return false;
            }
        }
        return true;
    }
};

07三数之和四数之和

Problem: 15. 三数之和

思路

这题难度很大,主要是双指针法不够熟悉。

  1. 固定一个指针,另外两个指针从左右两端开始往里靠,直到left==right
  2. 指针j设置为i+1,k设置为size-1,如果和大于0则右指针往左移动到下一个有效数字(不与上一位相同),如果小于0则left++到与left-1不重复的数字。如果等于0则两指针同时收缩到有效数字。
  3. 去重:去掉已经处理过的情况,比如i++之后的情况与i相同,那么就再i++

复杂度

时间复杂度:

添加时间复杂度, 示例: O(n)O(n)

空间复杂度:

添加空间复杂度, 示例: O(n)O(n)

Code

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int i, left, right;
        sort(nums.begin(), nums.end());
        vector<vector<int>> result;//vector二维矩阵
        for (i = 0; i < nums.size(); i++) {
            //跳过已经处理过的数字
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            left=i+1;
            right=nums.size()-1;
            while (left < right) {
                if (nums[i] + nums[left] + nums[right] == 0) {
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    //找到答案之后同时收缩
                    left++;
                    right--;
                    //移动到下一个有效的指针
                while (right > left && nums[right] == nums[right + 1]) right--;//移除0,0,2,2这类答案
                while (right > left && nums[left] == nums[left - 1]) left++;//移除0,0,2,2这类答案
                    
                }
                
                if (nums[i] + nums[left] + nums[right]>0){
                    right--;
                }else if(nums[i] + nums[left] + nums[right]<0){
                    left++;
                }
            }
        }
        return result;
    }
};

08四数之和

思路

这题思路和三数之和一样,嵌套了一个for循环,但是需要考虑到去重还有溢出的问题,所以十分复杂。

Code

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        int i, j, left, right;
        
        sort(nums.begin(), nums.end());
        vector<vector<int>> result;
        if(nums.size()<4){return result;}
        for (i = 0; i < nums.size() - 3; i++) {
            if(i>0 && nums[i]==nums[i-1]){continue;}//处理过的就不再处理
            for (j = i + 1; j < nums.size() - 2; j++) {
                if(j>(i+1) && nums[j]==nums[j-1]){continue;}
                left = j + 1;
                right = nums.size() - 1;
                while (left < right) {
                    if (target   == (long)nums[i] + nums[j] +nums[left] + nums[right]) {
                        result.push_back(
                            {nums[i], nums[j], nums[left], nums[right]});
                        left++;
                        right--;
                        while (left < right && nums[left] == nums[left - 1])
                            left++;
                        while (left < right && nums[right] == nums[right + 1])
                            right--;
                    }
                    if (target < (long long ) nums[i] + nums[j] + nums[left] + nums[right]) {
                        right--;
                    }
                    if (target > (long long ) nums[i] + nums[j] + nums[left] + nums[right]) {
                        left++;
                    }
                }
            }
        }
        return result;
    }
};