今天学习了哈希表的理论和相关应用
今天学习了有关哈希表的理论和应用,先是了解了什么是哈希表,其实数组就是一张哈希表。再讲到哈希表一般都是用来快速判断一个元素是否出现在集合里。那么哈希表怎么存放数据呢?由此引出了哈希函数,通过哈希函数,我们可以根据key映射到哈希表上我们要存放的位置。再映射的时候,会出现一个位置有多个元素的情况,这就是哈希碰撞,为了解决这个问题,我们通常使用拉链法和线性探测法解决哈希碰撞。常见的三种哈希结构有三种,分别是数组,set(集合),map(映射);切记,在做题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!
相关题目:
● 242.有效的字母异位词 :https://leetcode.cn/problems/valid-anagram/
● 349. 两个数组的交集 :https://leetcode.cn/problems/intersection-of-two-arrays/
● 202. 快乐数:https://leetcode.cn/problems/happy-number/
● 1. 两数之和 :https://leetcode.cn/problems/two-sum/
关于242.有效的字母异位词
题目描述
● 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
● 示例 1: 输入: s = “anagram”, t = “nagaram” 输出: true
● 示例 2: 输入: s = “rat”, t = “car” 输出: false
● 说明: 你可以假设字符串只包含小写字母。
● 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
该题的思路:
● 解法1:两层循环暴力破解,同时需要记录字符出现的次数,最后及逆行比较。
● 解法2:若s和t是字母异位词,则排序后两个字符数组是相等的,比较即可。
● 解法3:题目中字符串只有小写字符,则用26大小的数组即可记录,由于字符的特性,每个字符都对应一个ASCII值,故可以通过每个字符减去‘a’得到在数组的位置,而对应的值就是字符出现的次数。第一遍先遍历s字符串,将每个字符出现的次数记录在数组中,第二次遍历t字符串,减去相应的字符次数,最后通过检查数组的值是否全为0得出结果,若有不为0的值,则说明不是互为字母异位词。
解法3总结:
- 定义一个26大小的int数组记录字符的次数
- 先遍历s字符串,在数组上记录出现的字符次数
- 再遍历t字符串,再数组上减去出现的字符次数
- 检查数组上的值是否全为0,是则为字母异位词,反之不是
代码实现
//时间复杂度O(m+n) 空间复杂度O(1)
class Solution {
public boolean isAnagram(String s, String t) {
//定义一个数组进行存放英文字母 共26个
int[] record = new int[26];
//将s中的进行记录,下标记作字母,对应的值就是次数
for (int i = 0; i < s.length(); i++) {
record[s.charAt(i) - 'a']++;
}
//遍历t,将次数进行-1
for (int i = 0; i < t.length(); i++) {
record[t.charAt(i) - 'a']--;
}
//查看数组里面的组是否都为0,若为0则说明是
for (int count : record) {
if (count != 0) {
return false;
}
}
return true;
}
}
关于349. 两个数组的交集
题目描述
● 题意:给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
该题的思路:
● 题目特别说明输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序,则我们应该要想到用哈希法进行解题,我们可以选用数组,但如果数值较多较大,就不适合用数组解决,而应用另外一种结构 set来解决。而且在java中,hashset的值是唯一的,不重复的。
● 所以我们可以定义两个HashSet来解决问题,先循环数组1,将数组1中的元素加入HashSet1。
● 再循环数组2,查看HashSet1中是否存在该值,若存在,则将该值加入另一个HashSet2中,最后返回该HashSet2.
代码实现
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
//存放nums1元素的set集合
Set<Integer> numsSet = new HashSet<>();
//存放结果的set集合
Set<Integer> resultSet = new HashSet<>();
//循环加入
for (int num1 : nums1) {
numsSet.add(num1);
}
//检查是否有值,有就加入结果集合
for (int num2 : nums2) {
if (numsSet.contains(num2)){
resultSet.add(num2);
}
}
//转化为需要返回的类型
Object[] objects = resultSet.toArray();
int result[] = new int[objects.length];
for (int i = 0; i < objects.length; i++) {
result[i]=(int)objects[i];
}
return result;
}
}
关于202. 快乐数
题目描述
● 编写一个算法来判断一个数 n 是不是快乐数。
● 「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
● 如果 n 是快乐数就返回 True ;不是,则返回 False 。
● 示例:
● 输入:19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1
该题的思路:
题目有一个字眼很重要 无限循环,那么也就是说求和的过程中,sum会重复出现,所以我们需要保存曾经出现过的sum,并进行比较,这对解题很重要!
获取sum,即下一个数也是重中之重,我们可以写一个方法进行调用
故我们可以定义一个HashSet来保存sum,通过n分解平方求和求出sum,再次将sum当作n进行同样的操作,并不断比较该sum是否出现过,直到sum出现循环或者sum==1结束。
代码实现
class Solution {
public boolean isHappy(int n) {
//定义一个hashset记录sum的值
Set<Integer> record = new HashSet<>();
//如果n不为1并且sum不重复,就把sum,即n加入set,并不断循环下一次
while (n != 1 && !record.contains(n)) {
record.add(n);
n = getNextNumber(n);
}
return n == 1;
}
//根据n获取到sum,并再次当作n
private int getNextNumber(int n) {
int res = 0;
while (n > 0) {
int temp = n % 10;
res += temp * temp;
n = n / 10;
}
return res;
}
}
关于1. 两数之和
题目描述
● 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
● 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
● 示例:
● 给定 nums = [2, 7, 11, 15], target = 9
● 因为 nums[0] + nums[1] = 2 + 7 = 9
● 所以返回 [0, 1]
该题的思路:
当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法,题目要求数组中同一个元素不能使用两遍,而在两数之和中,我们可以用一个集合来存放我们遍历过的元素,然后在遍历数组的时候去询问这个集合,即边遍历边存放,边遍历边比较;而且,题目要求返回数组下标,则我们涉及到两种数值,那么我们可以使用map这种哈希表来解决,key代表数组的值,value代表该值所在的下标。
步骤:
- 定义一个map存放遍历过的值和下标
- 遍历数组,并用target值减去当前所在元素值,若结果在map中,则说明之前遍历过的值和当前值相加即为目标值,则取出下标返回即可
- 若结果不在map中,则将当前数组元素存入map
代码实现
class Solution {
public int[] twoSum(int[] nums, int target) {
//定义一个map存放遍历过的值和下标
HashMap<Integer, Integer> map = new HashMap<>();
int temp;
//存放结果下标的数组
int[] result = new int[2];
//遍历对比
for (int i = 0; i < nums.length; i++) {
//先求出当前元素还差多少才达到目标值
temp=target-nums[i];
//若遍历过的元素不存在该值,则将当前数组元素存入map
if (!map.containsKey(temp)){
map.put(nums[i],i);
}else {
//若存在该值,说明之前遍历过的值和当前值相加即为目标值,则取出下标返回即可
result[0]=i;
result[1]=map.get(temp);
return result;
}
}
return result;
}
}
第五天结束,第六天加油!