当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法
数据小用数组,数据大用set,数据比较散用map
一、242.有效的字母异位词
题目:242. 有效的字母异位词 - 力扣(LeetCode)
视频:学透哈希表,数组使用有技巧!Leetcode:242.有效的字母异位词_哔哩哔哩_bilibili
讲解:代码随想录
思路
a-z 的 ASCll 码是连续的,用字母减去 a 的 ASCll 码的就是每个字母的码
1.计数数组:使用一个长度为26的数组(假设字符串只包含小写字母)来记录每个字母出现的次数。
2. 统计字符出现次数:
- 遍历第一个字符串,将其中的每个字符出现的次数加1。
- 遍历第二个字符串,将其中的每个字符出现的次数减1。
3 检查计数数组:最后检查计数数组中的所有元素是否都为0。如果是,则说明两个字符串是字母异位词;否则不是。
charAt() 此方法返回位于字符串的指定索引处的字符。该字符串的索引从零开始。
class Solution {
public boolean isAnagram(String s, String t) {
int[] record = new int[26];
for(int i=0; i<s.length(); i++){
record[s.charAt(i) - 'a']++;
}
for(int i=0; i<t.length(); i++){
record[t.charAt(i) - 'a']--;
}
for(int count: record){
if(count != 0) return false;
}
return true;
}
}
二、349. 两个数组的交集
题目:349. 两个数组的交集 - 力扣(LeetCode)
视频:学透哈希表,set使用有技巧!Leetcode:349. 两个数组的交集_哔哩哔哩_bilibili
讲解:代码随想录
思路:
1、 初始化两个哈希集合:
- 第一个集合用于存储第一个数组中的所有元素。 set1
- 第二个集合用于存储两个数组的交集结果。reSet
2. 遍历第一个数组:
- 将第一个数组中的所有元素添加到第一个哈希集合中。
3. 遍历第二个数组:
- 对于第二个数组中的每个元素,检查它是否存在于第一个哈希集合中。
- 如果存在,则将其添加到第二个哈希集合中(为了避免重复,可以使用
add
方法,因为集合本身不允许重复元素)。
4. 返回结果:
- 将第二个哈希集合转换为数组并返回
HashSet 可以去重
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
if(nums1 == null || nums2 == null || nums1.length == 0 || nums2.length == 0) return new int[0];
//1
Set<Integer> set1 = new HashSet<>();
Set<Integer> reSet = new HashSet<>();
///2 遍历第一个数组,把它放到Set中
for(int i : nums1){
set1.add(i);
}
//3 遍历第二个数组,检查这个数组中是否存在于第一个哈希集合中,存在就放在新集合reSet中
for(int i : nums2){
if(set1.contains(i)){
reSet.add(i);
}
}
//4
return reSet.stream().mapToInt(x->x).toArray();
}
}
return resSet.stream().mapToInt(x -> x).toArray();
4、 这段代码 stream().mapToInt(x -> x).toArray() 是Java 8及以上版本中的一个流操作,用于将 Set 转换为 int[] 数组,具体解释如下:
- stream():将 Set 转换为一个流(Stream)。流是Java 8引入的一个高级迭代器,允许你以声明式方式处理数据集合。它支持多种操作,如过滤、排序、转换等。
- mapToInt(x -> x):这是一个中间操作,用于将流中的每个元素映射为一个 int 类型的值。这里的 x -> x 是一个Lambda表达式,表示将流中的每个元素 x 映射为它自身。由于 Set 中的元素已经是 Integer 类型,这个映射操作实际上没有改变元素的值,只是将 Integer 类型转换为 int 类型。
- toArray():这是一个终端操作,用于将流中的元素收集到一个数组中。由于前面的 mapToInt 已经将元素转换为 int 类型,所以 toArray() 会返回一个 int[] 数组。
第 4 步可以用另一种方法:
//4
int[] arr = new int[resSet.size()];
int j = 0;
for(int i : resSet){
arr[j++] = i;
}
return arr;
Set<Integer> set1 = new HashSet<>(); // 创建一个HashSet
set1.add(i); //往set1中加元素 将指定元素添加到HashSet中,如果元素已经存在,则不会重复添加
set1.contains(i) //判断set1中是否含有这个元素 判断HashSet是否包含指定元素。
三、202. 快乐数
视频:
讲解:代码随想录
这道题目使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。
判断sum是否重复出现就可以使用 HashSet。
class Solution {
public boolean isHappy(int n) {
Set<Integer> record = new HashSet<>();
while(n!=1 && !record.contains(n)){
record.add(n);
n = getNextNum(n);
}
return n == 1;
}
private int getNextNum(int n){
int result = 0;
while(n > 0){
int temp = n % 10;
result += temp * temp;
n = n/10;
}
return result;
}
}
逐位对数值进行平方计算:
以下是对getNextNum()这三行代码的逐行中文解释:
int d = n % 10;
这行代码的意思是取n
除以10的余数,并将其赋值给变量d
。这实际上是在获取n
的个位数字。例如,如果n
是123,那么n % 10
的结果就是3,所以d
会被赋值为3。
n = n / 10;
这行代码的意思是将n
除以10,并将结果重新赋值给n
。这一步是为了去掉n
的个位数字,以便在下一次循环中处理十位数字。继续上面的例子,如果n
原来是123,执行这行代码后,n
会变成12。
totalSum += d * d;
这行代码的意思是将d
的平方(即d * d
)加到totalSum
上。totalSum
是一个累加器,用于记录所有位数平方和的总和。在我们的例子中,如果d
是3,那么d * d
就是9,这个值会被加到totalSum
上。
综合这三行代码,它们的作用是取出一个整数n
的个位数字,计算其平方,并将这个平方值加到一个累加器totalSum
上,然后去掉n
的个位数字,以便处理下一个位数的数字。这个过程通常在一个循环中进行,直到n
变为0,这样就可以计算出n
的所有位数平方和的总和。
四、1. 两数之和
视频:梦开始的地方,Leetcode:1.两数之和,学透哈希表,map使用有技巧!_哔哩哔哩_bilibili
讲解:代码随想录
map中key和value分别表示什么?
这道题 我们需要 给出一个元素,判断这个元素是否出现过,如果出现过,返回这个元素的下标。
那么判断元素是否出现,这个元素就要作为key,所以数组中的元素作为key,有key对应的就是value,value用来存下标。
所以 map中的存储结构为 {key:数据元素,value:数组元素对应的下标}。
思路:
1. 初始化一个哈希表:用于存储数组中的元素及其对应的索引。
2. 遍历数组:
- 对于每一个元素
nums[i]
,计算出目标值与当前元素的差值temp = target - nums[i]
。 - 检查哈希表中是否存在
temp
,如果存在,则找到了两个数,返回它们的索引。 - 如果不存在,则将当前元素及其索引存入哈希表中。
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] result = new int[2];
if(nums == null || nums.length == 0) return result;
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++){
int temp = target - nums[i];
if(map.containsKey(temp)){
result[1] = i;
result[0] = map.get(temp);
break; //记得加break
}
map.put(nums[i], i);
}
return result;
}
}