本文写于我的另一篇文章算法学习-前缀和,学会了起码秒50题期间,发现哈希思想不止出现在前缀和的优化中,还经常出现在别的相关题目中,因此有必要针对这种方法好好做个总结。
基础知识
哈希表本质上就是需要理解Hash思想,即可以通过一个key找到一个value,他们俩是一一对应的关系。如何存储这个对应关系,可以通过两种实现方式。
- 通过HashMap容器存储,「容器动态增长」,key为索引值,value为索引对应的值,通过
map.get(key)
就能找到key对应的值。 - 通过数组实现哈希表,在索引范围不大的时候,将数组空间开到和索引范围相当,「容量固定」,通过下标随机查找。比方说对于英文26个字母的记录,我们可以
new int[26]
的数组,通过s.charAt(i)-'a'
找到某个字母对应在数组中的下标,从而进行存储与查找。
相关题目
1.两数之和
Hash表存储nums[i]
对应的下标i
,在搜索的过程中通过target-nums[i]
查找对应的下标。
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> map=new HashMap<>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(target-nums[i])){
return new int[]{map.get(target-nums[i]),i};
}
map.put(nums[i],i);
}
return new int[]{-1,-1};
}
}
1224.最大相等频率
参考题解,主要是采用哈希表记录相关信息,然后分类讨论更新结果。大体思想为,「前缀」提醒我们可以在用下标i
遍历数组的过程中,随时考虑起点到i
的情况。最长前缀的模式可以分为题解中的四种情况,通过哈希表记录,然后模式匹配,得出最长前缀长度。
class Solution {
public int maxEqualFreq(int[] nums) {
int maxlen=(int)1e5+10;
int[]numTime =new int[maxlen];//记录数字i出现的频度
int[]freTime =new int[maxlen];//记录出现频度为i的数字的总个数
int maxfre=0; //记录数字出现的最高频度
int n=0; //记录目前为止出现的不同数字个数
int ans=0;
for(int i=0;i<nums.length;i++){
int cur=nums[i];
//统计个数
if(numTime[cur]==0) n++;
numTime[cur]++;
maxfre=Math.max(maxfre,numTime[cur]);
//统计出现频度数字个数
freTime[numTime[cur]]++;
//如果原来已经出现该数字了,原来出现的频度的个数需要-1
if(numTime[cur]>1) freTime[numTime[cur]-1]--;
if(n==1||maxfre==1||freTime[maxfre]==1&&freTime[maxfre-1]==n-1||freTime[1]==1&&freTime[maxfre]==n-1){
//前缀长度为下标+1
ans=i+1;
}
}
return ans;
}
}
前缀和相关的题目
前缀和相关的题目中,一直通过哈希表建立快速查找的逻辑,相关题目参考我的另一篇原创文章算法学习-前缀和,学会了起码秒50题。