本文详细解答和思路来自于:
代码随想录 (programmercarl.com)
前导知识 - 哈希表
定义:哈希表是根据关键码的值而直接进行访问的数据结构.所谓的关键码就是下标实际上数组就是一张哈希表.
那么哈希表有什么作用呢?哈希表可以以O(1)的性能迅速定位你要找的元素,相较于依次遍历提升了很多性能,比如说你在公司里要查找一个人是否在公司,在查询的时候通常只需通过索引就可以知道该人是否在公司里.
所以哈希表通常使用于在集合中查询某个元素是否存在.
这个时候将公司成员映射到哈希表上,这就用到了哈希函数 (Hash function).
哈希碰撞
一般我们要存入哈希表的元素大小是小于哈希表的大小了该怎么办?或者是哈希函数给两个元素计算的的索引下相同怎么办?
我们刚刚说过,哈希表就是一个数组,那么就算哈希函数计算的再均匀,也会有元素无家可归,
两个元素去争一块空间的情况我们就成为哈希碰撞.
下面是哈希碰撞的两种解决方式:
1.链表法
在哈希表大小大于要存入数组的元素或者小于数组的元素都可以使用
操作如下:
2.线性探测法
只有在哈希表大小大于要存入元素大小的时候可以使用. 简单来说就是在后面再找一个空位来存放,比如说小李和小王算得的哈希值都是1,这时候小李先填入,再填入小王的时候发现2的位置没有值,那么小王就可以填入二号值.
Leetcode T242 相同字母的异序词
题目链接:
242. 有效的字母异位词 - 力扣(LeetCode)
1.思路
这里由于哈希表的元素是有限的,我们可以采用数组来记录数据,首先创建一个26个元素的数组record来记录每个字母的出现次数,先遍历第一个字符串,将对应下标的数据++,然后遍历第二个字符串,将对应位置的数组元素数据--,最后进行一次遍历record数组,如果找到了不是0的元素,那么就返回false,全是0则返回ture.
2.代码
class Solution {
public boolean isAnagram(String s, String t) {
int[] record = new int[26];
//创建记录数组
for(int i = 0;i<s.length();i++)
{
//进行目标映射
int tmp = (int)s.charAt(i)-'a';
record[tmp]++;
}
for(int i = 0;i<t.length();i++)
{
int tmp = (int)t.charAt(i)-'a';
record[tmp]--;
}
for(int j =0;j<record.length;j++)
{
if(record[j] != 0)
{
return false;
}
}
return true;
}
}
//如果我们想简洁一点,也可以使用增强for循环
for(int j:record)
{
if(j != 0)
{
return false;
}
}
return true;
Leetcode T349 两个数组的交集
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
1.题目思路
我们这里这道题一开始是没有给定nums1和num2的长度限制的,后来补上了限制,所以我们也可以用数组来实现,这里我们选择一个不同的方式来实现,我们知道HashSet是不存在重复数据的,所以这里我们使用HashSet来实现.
首先我们先判断两个数组是否为空数组,如果是空数组则可以直接返回.接着我们先创建两个HashSet:set1和set2,然后我们先遍历数组1,将数组1中的元素放入set1中,紧接着我们遍历第二个数组,如果数组中包含set1中的元素,就将该元素放入set2中,最后创建一个set2大小的数组用来返回.
2.代码实现
2.1 使用HashSet实现
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
if(nums1.length==0 || nums1==null ||nums2.length ==0 ||nums2 == null )
{
return new int[0];
}
Set<Integer> set1 = new HashSet<>();
Set<Integer> set2 = new HashSet<>();
for(int i = 0;i<nums1.length;i++)
{
set1.add(nums1[i]);
}
for(int i = 0;i<nums2.length;i++)
{
if(set1.contains(nums2[i]))
{
set2.add(nums2[i]);
}
}
int[] arr = new int[set2.size()];
int j = 0;
for(int i:set2)
{
arr[j++] = i;
}
return arr;
}
}
2.2 使用Hash数组实现
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
int[] hash1 = new int[1002];
int[] hash2 = new int[1002];
for(int i : nums1)
hash1[i]++;
for(int i : nums2)
hash2[i]++;
List<Integer> resList = new ArrayList<>();
for(int i = 0; i < 1002; i++)
if(hash1[i] > 0 && hash2[i] > 0)
resList.add(i);
int index = 0;
int res[] = new int[resList.size()];
for(int i : resList)
res[index++] = i;
return res;
}
}
LeetCode T202 快乐数
今天一点也不快乐!!!
题目链接:202. 快乐数 - 力扣(LeetCode)
1.题目思路
这题我们仍然可以使用HashSet来帮助我们判断一个元素是否出现过,所以在判断元素是否出现过的题目都可以使用哈希法.
首先我们先创建一个HashSet set,只要目前的n不是1并且HashSet没有包含这个数字就代表没有陷入循环,也没有满足快乐数的条件,这就是循环继续的条件,循环中我们只要把这个n加入我们的set中,并获取新的n即可,所以我们写一个get函数返回值是一个n.
题目要求每一位的平方相加得到新n,这里我们就采用取模再平方的方法实现,在循环外定义一个sum作为函数的返回值,取模一次n取到n的各位,再将个位的平方除以10,依次往复,直到<0为止.
2.代码实现
class Solution {
public boolean isHappy(int n) {
Set<Integer> set = new HashSet<>();
while(n != 1 && !set.contains(n))
{
set.add(n);
n = get(n);
}
return n==1;
}
int get(int n)
{
int sum = 0;
while(n>0)
{
int tmp = n%10;
sum += tmp*tmp;
n/=10;
}
return sum;
}
}
3.方法2:快慢指针法
快指针每次走两步,慢指针每次走一步,slow==fast为一个循环周期,判断是否为1引起的循环周期即可,是1就是快乐数,不是1就不是快乐数.
4.代码实现
class Solution {
public boolean isHappy(int n) {
int slow = n,fast = n;
do{
fast = get(get(fast));
slow = get(slow);
}while(slow != fast);
return fast==1;
}
int get(int n)
{
int sum = 0;
while(n>0)
{
int tmp = n%10;
sum += tmp*tmp;
n/=10;
}
return sum;
}
}
LeetCode T1 两数之和
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
1.思路分析
首先我么可以想到,两数之和,如果这里给定一个数,那么另一个数就是target-第一个数,因为最后要返回两个数下标形成的数组,所以首先我们先创建一个两个元素的数组res,然后这道题我们使用Map来完成,key是值,value是下标,先判断nums是否是空指针或者是空数组,
如果是就直接返回res数组,
如果不是我们就进行这样一个逻辑:用i遍历nums数组,创建变量
tmp = target-nums[i],然后判断我们的map中是否有一个值为tmp的键值对,没有就把这个键值对加入到map中,然后照这个逻辑循环.直到找到了,就将res数组的第一个元素赋值为i的下标,另一个元素则在map中寻找到tmp再找到他的下标,最后返回res数组.
2.代码实现
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
if(nums == null || nums.length == 0)
{
return res;
}
Map<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++)
{
int tmp = target-nums[i];
if(map.containsKey(tmp))
{
res[0] = i;
res[1] = map.get(tmp);
}
map.put(nums[i],i);
}
return res;
}
}
总结
在一个集合中查找某个元素是否存在就可以使用哈希表,当元素个数确定时,可以考虑使用哈希数组实现,当元素个数不确定时,可以考虑哈希表的其他实现类的结构,注意牢记哈希表的api方法.