C++哈希表二刷

哈希表

哈希表(又称散列表)是根据关键码的值而进行访问的数据结构。

哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素,如下图:

在这里插入图片描述

要查询一个名字是否在一所学校里。

枚举的时间复杂度是O(n),使用哈希表则为O(1)。

哈希函数

将学生姓名映射到哈希表上的的函数就是哈希函数。

会有一个取模的操作,防止学生数量大于哈希表的大小。

在这里插入图片描述

哈希碰撞

学生的数量大于哈希表的大小怎么办,此时就算哈希函数计算的再均匀,也避免不了会有几位学生的名字同时映射到哈希表 同一个索引下标的位置。这种现象叫做哈希碰撞。

解决方法:拉链法和线性探测法。

拉链法:将发生冲突的元素存储于链表中。

在这里插入图片描述

线性探测法:保证tableSize大于dataSize。

在这里插入图片描述

常见的三种哈希结构

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

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

数组见上一节。

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

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

可以看出:

  • 无前缀的都是有序的,数值也都是不可以重复的(map的底层实现以key作为红黑树的节点)。
  • multi则说明数值是可以重复的,unordered则说明底层实现是哈希表,排列是没有顺序的(自然数值不可以重复)。
  • 映射类型都是key value 的数据结构,以key作为节点来使用红黑树、哈希表实现的。

242.有效的字母异位词

**题意:**给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

示例 1: 输入: s = “anagram”, t = “nagaram” 输出: true

示例 2: 输入: s = “rat”, t = “car” 输出: false

说明: 你可以假设字符串只包含小写字母。

**解法:**数组。

注意养成赋初值的习惯,数组int a[26]是一定得赋初值的,vector则默认初值为0。

class Solution {
public:
    bool isAnagram(string s, string t) {
        if(s.size() != t.size()) return false;
        vector<int> a(26, 0); // 必须得养成赋初值的习惯 int a[26]={0};
        for(char c:s){
            a[c - 'a']++;
        }
        for(char c:t){
            a[c - 'a']--;
            if(a[c - 'a'] < 0) return false;
        }
        return true;
    }
};

349. 两个数组的交集

**题意:**给定两个数组 nums1nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序

**解法:**unordered_set法。一个us用于保存nums1,一个result_set用于记录us中与nums中重合的元素。(unordered_set在<unordered_set.h>头文件里)

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> us(nums1.begin(), nums1.end());
        unordered_set<int> result;
        for(int i = 0; i < nums2.size(); i++){
            if(us.find(nums2[i]) != us.end()){
                result.insert(nums2[i]);
            }
        }
        return vector<int> (result.begin(), result.end());
    }
};

202. 快乐数

题意:

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

**解法:**unordered_set法。一直算平方和,当出现us中出现的数字时,判断是不是等于1,等于1则为快乐数,不等于就不是。

to_string将int转为string,c - '0’可以将char转为int。

class Solution {
public:
    int calsquare(int n){
        string s = to_string(n);
        int result = 0;
        for(char c:s) result += (c - '0') * (c - '0');
        return result;
    }
    bool isHappy(int n) {
        unordered_set<int> us;
        while(true){
            us.insert(n);
            n = calsquare(n);
            if(us.find(n) != us.end()){
                if(n != 1) return false;
                else break;
            }
        }
        return true;
    }
};

1. 两数之和

题意:

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

**解法:unordered_map法。**因为只有唯一解,选用unordered_map。key存nums的值,value存序号。然后解的时候用当前的nums[i]去对比历史存储的unordered_map中的元素。

pair的两种表示:{a, b}、pair<int, int>(a, b)。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> umap;
        for(int i = 0; i < nums.size(); i++){
            auto search = umap.find(target - nums[i]);
            if(search != umap.end()){
                return {search->second, i};
            }
            umap.insert(pair<int, int>(nums[i], i));
        }
        return {};
    }
};

454.四数相加II

题意:

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

**解法:**类似两数之和。unordered_map法。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        // 需要查询的话,无序找起来才快,所以不用multiset而用unordered_map
        unordered_map<int, int> umap;
        for(int a : nums1){
            for(int b : nums2){
                umap[a + b]++;
            }
        }
        int count = 0;
        for(int c : nums3){
            for(int d : nums4){
                if(umap.find(0 - (c + d)) != umap.end()){
                    count += umap[0 - (c + d)];
                }
            }
        }
        return count;
    }
};

赎金信

**题意:**给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

**解法:**哈希表,用的数组。

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int a[26] = {0};
        for(char c : magazine) a[c - 'a']++;
        for(char c : ransomNote){
            a[c - 'a']--;
            if(a[c - 'a'] < 0) return false;
        }
        return true;
    }
};

15. 三数之和

**题意:**给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

**解法:**双指针法。先把数组排序后,i作为for循环第一层,left从i+1开始,right从num.size()-1开始。

在这里插入图片描述

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        for(int i = 0; i < nums.size(); i++){
            if(nums[i] > 0) return result;
            if(i > 0 && nums[i] == nums[i - 1]) continue; // 注意是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 {
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    while(nums[right] == nums[right - 1] && left < right) right--;
                    while(nums[left] == nums[left + 1] && left < right) left++;
                    right--;
                    left++;
                }
            }
        }
        return result;
    }
};

18. 四数之和

**题意:**给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:

答案中不可以包含重复的四元组。

示例: 给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]

**解法:**双指针法。双指针法可以将方法的时间复杂度降一个指数幂,在本题就是将O(n4)转为O(n3)。注意while去重合的时候要先判断left<right,避免nums[right - 1]或者nums[left + 1]的下标超了。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        for(int i = 0; i < nums.size(); i++){
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            for(int j = i + 1; j < nums.size(); j++){
                if(j > i + 1 && nums[j] == nums[j - 1]) continue;
                int left = j + 1;
                int right = nums.size() - 1;
                while(left < right){
                    if((long) nums[i] + nums[j] + nums[left] + nums[right] > target) right--;
                    else if((long) nums[i] + nums[j] + nums[left] + nums[right] < target) left++;
                    else{
                        result.push_back(vector<int>{nums[i], nums[j], nums[left], nums[right]});
                        while(left < right && nums[right] == nums[right - 1]) right--;
                        while(left < right && nums[left] == nums[left + 1]) left++;
                        right--;
                        left++;
                    }
                }
            }
        }
        return result;
    }
};

参考:
1、代码随想录/哈希表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值