首页 > 其他分享 >3 哈希表

3 哈希表

时间:2023-07-19 16:47:00浏览次数:31  
标签:right nums int ## 哈希 left

# 哈希表
## 1 哈希表理论基础
### 1.1 什么是哈希表
> 哈希表(hash table):根据关键码的值而直接进行访问的数据结构。
![img](/i/l/?n=23&i=blog/3237570/202307/3237570-20230719105359176-1676757602.png)

- hash表解决问题:快速判断一个元素是否出现集合里

- hash函数:哈希函数如下图所示,通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值
![img](/i/l/?n=23&i=blog/3237570/202307/3237570-20230719105649995-1463032782.png)
此时如果如果hashCode得到的数值大于哈希表的大小,也就是大于tableSize,保证映射出来的索引数值都落在哈希表上,我们会在再次对数值做取模的操作,但是如果数量本身就大于哈希表的大小,则会出现哈希碰撞
- hash碰撞:
    ![img](/i/l/?n=23&i=blog/3237570/202307/3237570-20230719105859382-945181661.png)
    一般哈希碰撞有两种解决方法, 拉链法和线性探测法。
    - 拉链法
        在碰撞的位置的元素都存储在链表中,这样就可以通过索引找到相关的元素。
        拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。
    - 线性探测法     使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。冲撞的位置直接向下寻找空位安放。
- 常见的哈希结构
    - 数组     - set 集合     - map 映射
> std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。
> std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。
当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。
# 2 有效的字母异位词
## 题目
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1: 输入: s = "anagram", t = "nagaram" 输出: true
示例 2: 输入: s = "rat", t = "car" 输出: false
说明: 你可以假设字符串只包含小写字母。
## 思路 定义数组作为哈希表,因为字母只有26个,一一对应的代价很小,哈希映射函数为ascii码的顺序对应字母顺序,两个循环在s字符串循环对应+1,t字符串循环对应-1,最终数组全为0就是false,否则true
## 代码 ```C++ bool isAnagram(string s, string t) {     int record[26] = {0};     for(int i = 0;i < s.size(); i++){         record[s[i]-'a']++;     }     for(int i = 0;i < t.size(); i++){         record[t[i]-'a']--;     }     for(int i = 0; i < 26; i++){         if(record[i] != 0) return false;     }     return true; } ```
# 3 两个数组的交集
## 题目 题意:给定两个数组,编写一个函数来计算它们的交集。
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
说明: 输出结果中的每个元素一定是唯一的。 我们可以不考虑输出结果的顺序
## 思路
std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希表, 使用unordered_set 读写效率是最高的,并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set。
## 代码 ```C++ vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {     unordered_set<int> result_set;     unordered_set<int> nums1_set(nums1.begin(),nums1.end());     for(int num:nums2){         if(nums1_set.find(num)!=nums1_set.end()){             result_set.insert(num);         }     }     return vector<int>(result_set.begin(),result_set.end()); } ```

# 4 快乐数
## 题目 编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为  1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False 。
示例:
输入:19 输出:true 解释: 1^2 + 9^2 = 82 8^2 + 2^2 = 68 6^2 + 8^2 = 100 1^2 + 0^2 + 0^2 = 1
## 思路
判断是否循环,循环就不是快乐数,当看到是否有重复时就想到哈希法,终止条件:重复或者和为1
## 代码 ```C++ bool isHappy(int n) {     unordered_set<int>happySet;     int happyNum = 0;     while(happyNum != 1){         if(happySet.find(n)!=happySet.end()) return false;         happySet.insert(happyNum);         happyNum = 0; //重置以重新计算         while(n != 0){             happyNum += (n % 10) * (n % 10);             n /= 10;         }         n = happyNum;     }     return true; } ```
# 5  两数之和
## 题目
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

## 思路
我们不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适。
## 代码 ```C++ vector<int> twoSum(vector<int>& nums, int target) {     unordered_map<int,int> map;     for(int i=0;i < nums.size();i++){         auto iter = map.find(target - nums[i]);         if(iter != map.end()) return {iter->second,i};         map.insert(pair<int,int>(nums[i],i));     }     return {}; } ```
注意map的使用方式

# 6 四数相加II
## 题目
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -2^28 到 2^28 - 1 之间,最终结果不会超过 2^31 - 1 。
## 思路
map,key为两数之和,value为出现的次数,统计出现次数即可
## 代码 ```C++ int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {     unordered_map<int,int> map;     for(int num1 : nums1){         for(int num2 :nums2){             map[num1 + num2]++;         }     }     int count = 0;     for(int num3 : nums3){         for(int num4 :nums4){             if(map.find(-(num3 + num4)) != map.end()) count+=map[-(num3 + num4)];         }     }     return count; } ```
# 7 赎金信
## 题目
给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。
(题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。杂志字符串中的每个字符只能在赎金信字符串中使用一次。)
## 思路 跟有效字母异味词相似,但是这里只需要管ransomnote在magazine中够出现就行。int一个record[26]数组存储字母个数。
## 代码 ```C++ int record[26]={0};     for(int i =0;i<magazine.size();i++){         record[magazine[i]-'a']++;     }     for(int i = 0;i < ransomNote.size() ; i++){         record[ransomNote[i]-'a']--;     }     for(int i = 0 ;i<26;i++){         if(record[i]<0) return false;     }     return true; ```
# 8 三数之和
## 题目
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
## 思路 可以使用哈希法,但是操作比较困难,两个for循环,主要要去重需要很多判断。这里可以使用双指针法,更加高效

## 代码 ```C++ vector<vector<int>> threeSum(vector<int>& nums) {     vector<vector<int>> result;     sort(nums.begin(),nums.end());     for(int i = 0 ; i < nums.size() - 2 ; i++){         if(nums[i] > 0) return result;         if(i>0 && nums[i] == nums[i - 1]) continue;         int right = nums.size() - 1;         int left = i + 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({nums[i],nums[left],nums[right]});                 while(right>left && nums[right] == nums [right - 1]) right--;                 while(right>left && nums[left] == nums [left + 1]) left--;                 left++;                 right--;             }         }     }     return result; } ```
其中的去重操作细节,在排序之后,相邻的相同数字跳过,但又需要考虑(-1,-1,2)这种情况,所以`if(i>0 && nums[i] == nums[i - 1]) continue;`

# 9 四数之和
## 题目
题意:给定一个包含 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] ]
## 思路
同样双指针,多一层for循环
## 代码 ```C++ 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() - 3 ; i++){         if(nums.size() < 4) return result;         for(int j = 1;j < nums.size() - 2 ; j++){             if(i >= j || (i >= 1 && nums[i] == nums[i-1]) ) continue;             if(j > i + 1 && nums[j] == nums[j-1]) continue;             int right = nums.size() - 1;             int left = j + 1;             while(left < right){                 if((long)nums[i]+nums[j]+nums[right]+nums[left]>target) {                     right--;                 }                 else if((long)nums[i]+nums[j]+nums[right]+nums[left]<target){                     left++;                 }                 else{                     result.push_back({nums[i],nums[j],nums[left],nums[right]});                     while(right>left&&(nums[right]==nums[right-1])){                         right--;                     }                     while(right>left&&(nums[left]==nums[left+1])){                         left++;                     }                     left++;                     right--;                 }             }         }     }     return result; } ```
- 注意点1:可能越界,超过int指针最大值 - 注意点2:保证i < j,并且对于i和j都要剪枝

标签:right,nums,int,##,哈希,left
From: https://www.cnblogs.com/mobbu/p/17566015.html

相关文章

  • 字符串哈希
    题目描述给定一个长度为n的字符串,再给定m个询问,每个询问包含四个整数l1,r1,l2,r2,请你判断[l1,r1]和[l2,r2]这两个区间所包含的字符串子串是否完全相同。字符串中只包含大小写英文字母和数字。输入格式第一行包含整数n和m,表示字符串长度和询问次数。第二行包含一个长度为......
  • 哈希数据结构
    参考链接:https://blog.csdn.net/CRMEB/article/details/1208206821.哈希表哈希表,即散列表(Hashtable),是根据keyvalue而直接进行访问的数据结构。也就是说,它通过把keyvalue映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。......
  • docker分布式存储之哈希槽3主3从redis集群配置+主从扩容缩容
    创建开启六台redis容器systemctlrestartdockerdockerpullredis:6.0.8根据需求下载redis的镜像版本配置3主3从开启六台redis容器分别用node-1~node-6来区分dockerrun-d--nameredis-node-1--nethost--privileged=true-v/tmp/redis/share/redis-node......
  • Perl学习笔记2_标量数组哈希
    1.概述Perl是弱类型语言,变量不需要指定类型,解释器根据上下文自动选择匹配类型.Perl有三个基本的数据类型:标量($),数组(@),哈希(%).2.标量,scalar标量变量以$标记.my$a=123;#数字my$b="123";#字符串my$c=0x1F;#16进制my$d=047;#8进制my$e......
  • LeetCode 519. Random Flip Matrix 哈希Map
    Thereisanmxnbinarygridmatrixwithallthevaluesset0initially.Designanalgorithmtorandomlypickanindex(i,j)wherematrix[i][j]==0andflipsitto1.Alltheindices(i,j)wherematrix[i][j]==0shouldbeequallylikelytobereturne......
  • java哈希取模例子
    Java哈希取模示例流程概述在介绍如何实现Java哈希取模例子之前,我们需要了解一下整个流程。哈希取模是一种常见的数据处理技术,用于将数据分散到固定大小的哈希表或数组中。下面是实现Java哈希取模的基本流程:创建一个哈希表或数组,用于存储数据。将输入的数据进行哈希运算,得到一......
  • .NET Core应用程序每次启动后使用string.GetHashCode()方法获取到的哈希值(hash)不相
    前言如标题所述,在ASP.NETCore应用程序中,使用string.GetHashCode()方法去获取字符串的哈希值,但每次重启这个ASP.NETCore应用程序之后,同样的字符串的哈希值(hash)但不相同了。这是什么意思呢?具体的应用场景是这样的:项目中有一张表的某个字段保存了类似URL这样的字符串,这张表......
  • 哈希(md5)绕过
    MD5形式MD5一共128位,内容由0-9之间的数字和a-f之间的小写字母组成右边是一个MD5:d41d8cd98f00b204e9800998ecf8427e,共32个字符(一个字符4位)绕过0e绕过原理:0e开头的字符串在参与比较时,会被当做科学计数法,结果转换为0#生成0e开头+后面全是数字的md5编码的字符串的python脚本:......
  • 有效的字母异位词,哈希表方法
    /***有效的字母异位词*力扣题目链接(opensnewwindow)**给定两个字符串s和t,编写一个函数来判断t是否是s的字母异位词。**示例1:输入:s="anagram",t="nagaram"输出:true**示例2:输入:s="rat",t=......
  • 哈希传递
    哈希传递简介PassTheHash即PTH,就是通过传递Windwos本地账户或者域用户的hash值,达到控制其他服务器的目的在进入企业内网之后,如果是WindowsPC或者服务器较多的环境,极有可能会使用到hash传递来进行内网的横传,现在企业内部一般对于口令强度均有一定的要求,抓取到本地hash后可......