问:什么时候想到用哈希法?
答:当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法!
题目链接:242. 有效的字母异位词 - 力扣(LeetCode)
给出两种使用不同集合来解决这个问题:
1) 使用数组来解决这个问题。由于字母异位词涉及字符的频率比较,并且假设输入的字符串只包含小写字母(a-z),我们可以使用一个长度为 26 的数组来记录每个字符的频率。
2)使用HashMap来解决这个问题。使用两个哈希表分别统计两个字符串中每个字符的出现频率。最后,比较这两个哈希表,如果它们相等,则这两个字符串是字母异位词。
class Solution242 {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()) return false;
// 创建一个长度为 26 的数组来存储每个字母的频率
int[] count = new int[26];
// 遍历第一个字符串,增加字符频率
for (int i = 0; i < s.length(); i++) {
count[s.charAt(i) - 'a']++;
}
// 遍历第而个字符串,减少字符频率
for (int i = 0; i < t.length(); i++) {
count[t.charAt(i) - 'a']--;
}
// 检查频率数组是否所有值都是 0
for (int i = 0; i < count.length; i++) {
if(count[i] != 0)
return false;
}
return true;
}
// 使用HashMap解决
public static boolean isAnagram2(String s, String t){
// 如果两个字符串长度不相等,它们不可能是字母异位词
if (s.length() != t.length()) {
return false;
}
// 创建两个哈希表来存储字符出现的频率
Map<Character, Integer> countS = new HashMap<>();
Map<Character, Integer> countT = new HashMap<>();
// 遍历第一个字符串,统计每个字符出现的次数
for (char c : s.toCharArray()) {
countS.put(c, countS.getOrDefault(c, 0) + 1);
}
// 遍历第二个字符串,统计每个字符出现的次数
for (char c : t.toCharArray()) {
countT.put(c, countT.getOrDefault(c, 0) + 1);
}
// 比较两个哈希表,如果相等,则是字母异位词
return countS.equals(countT);
}
}
题目链接:349. 两个数组的交集 - 力扣(LeetCode)
使用集合Set来存储元素,集合的性质使得相同的元素只会存储一次。使用一个 HashSet
来存储第一个数组 nums1
中的所有元素。然后,我们遍历第二个数组 nums2
,检查其中的每个元素是否存在于第一个集合中。如果存在,我们将该元素添加到另一个 HashSet
中,这个集合用于存储交集的结果。最后,我们将交集的集合转换为一个数组,并返回这个数组作为结果。
class Solution349 {
public int[] intersection(int[] nums1, int[] nums2) {
// 使用HashSet来存储nums1中的元素
Set<Integer> set1 = new HashSet<>();
for (int num : nums1) {
set1.add(num);
}
// 使用另一个HashSet来存储交集的元素
Set<Integer> intersection = new HashSet<>();
for (int num : nums2) {
if(set1.contains(num)){
intersection.add(num);
}
}
// 将交集结果转换为数组
int[] res = new int[intersection.size()];
int index = 0;
for (Integer num : intersection) {
res[index++] = num;
}
return res;
}
}
- 时间复杂度:O(n+m)
- 空间复杂度:O(1)
题目链接:202. 快乐数 - 力扣(LeetCode)
题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要!
我们使用一个 HashSet
来存储已经见过的数字。如果在计算过程中出现了重复的数字,说明进入了无限循环,此时可以判断该数不是快乐数。
我们不断地计算当前数字的下一步,如果计算得到 1,则该数是快乐数;如果出现重复的数字,则该数不是快乐数。
另外还需注意如何计算每个数字的平方和。
19 是一个快乐数,因为 19 -> 82 -> 68 -> 100 -> 1
2 不是一个快乐数,因为它会进入循环 2 -> 4 -> 16 -> 37 -> 58 -> 89 -> 145 -> 42 -> 20 -> 4
class Solution202 {
public boolean isHappy(int n) {
// 使用HashSet来存储已经见过的数字,检测循环
Set<Integer> seenNumbers = new HashSet<>();
// 当n不是1,且没有出现循环数字时,继续计算
while (n != 1 && !seenNumbers.contains(n)){
seenNumbers.add(n);
n = getNext(n);
}
return n == 1;
}
private static int getNext(int n){
int totalSum = 0;
// 逐位处理数字
while (n > 0){
// 取当前数字的最后一位
int digit = n % 10;
totalSum += digit * digit;
// 去掉最后一位
n /= 10;
}
return totalSum;
}
}
- 时间复杂度:O(log n)
- 空间复杂度:O(1)
题目链接:1. 两数之和 - 力扣(LeetCode)
为了找到和为目标值的两个整数,并返回它们的数组下标,我们可以使用哈希表来实现,这样可以将时间复杂度降低到 O(n)。
class Solution1 {
public int[] twoSum(int[] nums, int target) {
// 创建一个哈希表来存储数值和对应的下标
Map<Integer, Integer> numMap = new HashMap<>();
// 遍历数组
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
// 检查哈希表中是否存在当前数的补数
if(numMap.containsKey(complement)){
// 如果存在,返回补数下标和当前数下标
return new int[] {numMap.get(complement), i};
}
// 如果不存在,将当前数及其下标存入哈希表
numMap.put(nums[i], i);
}
// 按题目要求,假设每种输入只会对应一个答案,所以这里不需要返回值
throw new IllegalArgumentException("No two sum solution");
}
}
需要思考的四个点:
- 为什么会想到用哈希表?
- 哈希表为什么用map?
- 本题map是用来存什么的?
- map中的key和value用来存什么的?