今日总结:今天是关于哈希表的题目,总体难度不算太大,今天准备再写一篇关于 Vuex 的博客。
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的字母异位词。
**注意:**若 s
和 t
中每个字符出现的次数都相同,则称 s
和 t
互为字母异位词。
示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
示例 2:
输入: s = “rat”, t = “car”
输出: false
哈希表(Hash Table)是一种数据结构,用于实现字典(Dictionary)和映射(Map)等抽象数据类型。它通过哈希函数将键(key)映射到哈希表中的特定位置,以便快速定位和检索值(value)。哈希表的主要思想是将键转换为索引,使得在理想情况下可以在常量时间内(O(1) 的时间复杂度)查找或插入元素。
哈希表的核心组成部分包括:
- 哈希函数(Hash Function): 这是哈希表的关键部分,它将键转换为哈希码或哈希值。理想的哈希函数应该将不同的键映射到不同的哈希值,尽可能避免冲突(多个键映射到同一个位置)。
- 哈希表或哈希桶(Hash Table / Hash Bucket): 这是存储实际数据的地方。哈希表由一个数组构成,每个位置通常被称为“桶”,存储着键值对或者指向键值对的指针。
- 解决冲突的方法: 当不同的键经过哈希函数映射到相同的位置时,就会发生冲突。解决冲突的方法有很多种,常见的包括开放定址法(如线性探测和二次探测)和链表法(或者更高效的形式,如使用红黑树)。
哈希表一般用于快速的判断一个元素是否出现在集合里。
对于上面的题目暴力解法就是两层 for
循环,记录字符是否重复出现,时间复杂度为 O(n^2)
这道题可以通过哈希表的方式解题,因为上面提到,哈希表可以快速的判断一个元素是否出现在集合里,所以如果两个字符串的长度相同,且各个字母的出现次数也相同就说明他们两个互为字母异位词。
我们知道,小写字母的 ASCII 码值是相邻的,一个长度为 26 的数组就可以存储这些字母出现的频率,将数组的下标作为索引,将数组元素值作为值就可以快速的查找。
首先判断两个字符串的长度是否相同,如果不同直接返回 false
之后同时循环遍历两个数组,如果字符串 1 中出现了就将索引加一,字符串 2 中出现则减一,最后我们去检查这个数组,如果出现了非 0 的值,说明这两个字符串不是互为异位词。
class Solution {
public boolean isAnagram(String s, String t) {
// 将字符串转化为字符数组
char[] charArray1 = s.toCharArray();
char[] charArray2 = t.toCharArray();
int length = charArray1.length;
// 检测长度
if (charArray2.length != length) {
return false;
}
int[] hash = new int[26]; // 哈希数组
for (int i = 0; i < length; i++) {
hash[charArray1[i] - 'a']++;
hash[charArray2[i] - 'a']--;
}
for (int i = 0; i < hash.length; i++) {
if (hash[i] != 0) {
return false;
}
}
return true;
}
}
给定两个数组 nums1
和 nums2
,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的
提示:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
也是一道非常简单的问题,和上面题目的思想相同,我们先来看数组的解法,之后再通过集合去简化。
题目中已经限定了数组中元素的大小,所以我们直接将哈希数组声明为长度为 1000
的数组即可,这道题易错点是是否考虑了去重,所以我们遍历第一个数组,将出现的数字作为索引,值我们统一设为 1
,再去遍历第二个数组,这时候就要注意了,我们必须保证结果集中只添加了一次,所以给出如下的判断语句:
if (hash[nums2[i]] == 1 && ++hash[nums2[i]] == 2) {
list.add(nums2[i]);
}
只能是 2
的时候再接收,如果后续为 3
或者其他数字说明我们加重了。
class Solution {
int[] hash = new int[1000]; // 哈希数组
public int[] intersection(int[] nums1, int[] nums2) {
List<Integer> list = new ArrayList<>(); // 结果列表
for (int i = 0; i < nums1.length; i++) {
hash[nums1[i]] = 1;
}
for (int i = 0; i < nums2.length; i++) {
// 保证不会重复
if (hash[nums2[i]] == 1 && ++hash[nums2[i]] == 2) {
list.add(nums2[i]);
}
}
// 将结果转化为数组
int index = 0;
int res[] = new int[list.size()];
for(int i : list)
res[index++] = i;
return res;
}
}
import java.util.HashSet;
import java.util.Set;
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
return new int[0];
}
Set<Integer> set1 = new HashSet<>();
Set<Integer> resSet = new HashSet<>();
//遍历数组1
for (int i : nums1) {
set1.add(i);
}
//遍历数组2的过程中判断哈希表中是否存在该元素
for (int i : nums2) {
if (set1.contains(i)) {
resSet.add(i);
}
}
//方法1:将结果集合转为数组
return resSet.stream().mapToInt(x -> x).toArray();
//方法2:另外申请一个数组存放setRes中的元素,最后返回数组
int[] arr = new int[resSet.size()];
int j = 0;
for(int i : resSet){
arr[j++] = i;
}
return arr;
}
}
和上面数组的解法时间是略高于前面的方法的,因为题目中限制了数字的大小,但如果很大的话,集合解法是优于数组的。
编写一个算法来判断一个数 n
是不是快乐数。
「快乐数」 定义为:
如果 n
是 快乐数 就返回 true
;不是,则返回 false
。
示例 1:
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
示例 2:
输入:n = 2
输出:false
也是一道比较简单的题目,唯一的难点可能就是如何求各位置上的平方和
我们来分析一下这道题目,一个数字只有两种情况:1. 经过有限次的平方加和变为了 1
2. 陷入循环,那我们如何判断是否陷入了循环呢?
就看一个和是否出现过,如果出现过两次则说明一定陷入了循环。
理解了这些,代码就非常好写了
class Solution {
public boolean isHappy(int n) {
Set<Integer> record = new HashSet<>();
int x = 0; // 每一位上的数字
int sum = 0; // 每一次循环的和
// 检测是否重复或者为 1
while (sum != 1 && !record.contains(n)) {
x = 0;
sum = 0;
record.add(n); // 放入集合中
while (n > 0) {
x = n % 10;
sum += x * x;
n = n / 10;
}
n = sum;
}
return sum == 1;
}
}
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
又是这道经典的梦开始的地方,这里我们来介绍这道题的哈希解法.
回顾一下,什么时候用哈希表?就是我们判断一个数在之前有没有出现过,在这道题里面,我们要判断的就是target - nums[i]
在我们之前的遍历中是否出现过,如果出现过就表明我们找到了这个组合,将这两个下标直接返回即可
我们可以通过 Map
来同时存储数字的下标和数值,也可以直接从头开始遍历求解.
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 temp = target - nums[i]; // 遍历当前元素,并在map中寻找是否有匹配的key
if(map.containsKey(temp)){
res[1] = i;
res[0] = map.get(temp);
break;
}
map.put(nums[i], i); // 如果没找到匹配对,就把访问过的元素和下标加入到map中
}
return res;
}
}