目录
今日学习的文章
哈希表理论基础
建议:大家要了解哈希表的内部实现原理,哈希函数,哈希碰撞,以及常见哈希表的区别,数组,set 和map。
什么时候想到用哈希法,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。 这句话很重要,大家在做哈希表题目都要思考这句话。
文章讲解:代码随想录
有效的字母异位词
建议: 这道题目,大家可以感受到 数组 用来做哈希表 给我们带来的遍历之处。
题目链接/文章讲解/视频讲解: 代码随想录
看到题目的第一想法
判断是否存在异位词
1 我首先想到用一个数组记录所有26个字母出现的次数,定义一个int数组,长度为0~26,每个下标分别对应a~z
2 两个字符串用两个数组来记录,最后用一个数组来判断值是否相等
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
这道题就是判断一个字符串中的字符是否在另一个字符串中出现过
看到代码随想录之后的想法
代码随想录和我的想法不尽相同,但是代码随想录的想法更加优化
1 代码随想录只用了一个数组,当遍历第一个字符串时,把对应元素的值+1,然后当遍历第二个字符串时,把对应位置的值-1,得出一个最终数组,看最终数组中每个值是否为0
2 关于字符对应数组的下标,我们不需要去记住'a'的ASCII码值,只需要用对应的char-'a' 对应的字符自然而然就是下标了
自己实现过程中遇到的困难
1 空间复杂度过高,我用了三个数组,代码随想录只用了一个数组,当遍历第一个字符串时,把对应元素的值+1,然后当遍历第二个字符串时,把对应位置的值-1,得出一个最终数组,看最终数组中每个值是否为0
2 关于字符对应数组的下标,我想的时是用每个字符减去对应的ASCII码值来一一对应,太麻烦,我们不需要去记住'a'的ASCII码值,只需要用对应的char-'a' 对应的字符自然而然就是下标了
3 字符的String.charAt(index)可以获取对应的字符
/*class Solution {
public boolean isAnagram(String s, String t) {
int[] charArrays = new int[26];
int[] charArrayt = new int[26];
//如果是A则插入0,如果是B则插入1
//数组中每个值都为0
//差点去记住char的ascII这样是不对的
for(int i=0;i<s.length();i++){
int indexs = s.charAt(i)-'a';
charArrays[indexs]++;
}
for(int j=0;j<t.length();j++){
int indext = t.charAt(j)-'a';
charArrayt[indext]++;
}
//判断两个数组值是否相同
for(int z=0;z<26;z++){
if(charArrayt[z]!=charArrays[z]){
return false;
}
}
return true;
}
}*/
class Solution {
public boolean isAnagram(String s, String t) {
int[] charArrays = new int[26];
//第一个先加,第二个再减,看是否全部为0
//用char-'a' 来判断下标从而不需要判断ascII码值
//s.length() 来获取长度,s.charAt(i) 来获取下标
//当我们遇到了要快速判断一个元素是否出现集合里的时候,判断某一个元素是否在集合里出现过,就要考虑哈希法了,用哈希数据结构辅助。
// 遇到哈希想数组(数值很小) map(key和value) 和set(数值很大)
for(int i = 0;i< s.length();i++){
charArrays[s.charAt(i)-'a']++;
}
for(int j = 0;j< t.length();j++){
charArrays[t.charAt(j)-'a']--;
}
for(int z = 0;z<charArrays.length;z++){
if(charArrays[z]!=0){
return false;
}
}
return true;
}
}
两个数组的交集
建议:本题就开始考虑 什么时候用set 什么时候用数组,本题其实是使用set的好题,但是后来力扣改了题目描述和 测试用例,添加了 0 <= nums1[i], nums2[i] <= 1000 条件,所以使用数组也可以了,不过建议大家忽略这个条件。 尝试去使用set
。
题目链接/文章讲解/视频讲解:代码随想录
给定两个数组 nums1
和 nums2
,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序
看到题目的第一想法
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
判断nums1是否在nums2中出现过
1 用两个set 将nums1 和nums2去重,然后再放入一个Result中进行终极去重,最后再转换为int返回
看到代码随想录之后的想法
代码随想录比我的方法优化太多
1 总共只需要两个set,第一个set对nums1 进行去重,第二个resultSet,通过set1.contains 来判断数据是否存在于nums1中,如果存在则放入resultSet中(本质就是看第二个数组中的元素是否在数组1中存在,如果存在就放入resultSet)
自己实现过程中遇到的困难
1 set有很多api不是很熟练,感觉目前用的最多的是set.contains
2 set转换成int数组也不是很熟练,代码随想录中给出了比较好的办法,在下图代码中
import java.util.HashSet;
import java.util.Set;
/*class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
//输出是唯一的,则放入set中
//用两个set 把数据存入 用两个for循环
Set set1 = new HashSet<Integer>();
Set set2 = new HashSet<Integer>();
for(int i=0;i<nums1.length;i++){
set1.add(nums1[i]);
}
for(int i=0;i<nums2.length;i++){
set2.add(nums2[i]);
}
Set resultSet = new HashSet<Integer>();
//toArray使用不了
int[] int1 = set1.toArray();
int[] int2 = set2.toArray();
for(int i=0;i<int1.length;i++){
for(int j=0;i<int2.length;j++){
if(int1[i] == int2[j]){
resultSet.add(int1[i]);
break;
}
}
}
//这个toArray 不知为何用不了,但是在这里应该不需要使用 toArray
return resultSet.toArray();
}
}*/
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
//代码随想录中 用一个set存nums1 另外一个直接作为resultSet,存nums2 和 set1中的元素
//这里用到了set的contains方法,set中方便查询,contains时间复杂度快
Set set1 = new HashSet<Integer>();
Set<Integer> resultSet = new HashSet<Integer>();
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])){
resultSet.add(nums2[i]);
}
}
// return resSet.stream().mapToInt(x -> x).toArray();也可以用这个方法
int[] resultInt = new int[resultSet.size()];
int j = 0;
//把resultSet 转换为数组进行返回 这个对于java的增强for遍历技巧可以学一下
for(int i:resultSet){
resultInt[j++] = i;
}
return resultInt;
}
}
看到题目的第一想法
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
题干中给出了一个无限循环的提示,其实就是告诉我们,计算出的数据可能之前会出现过
我看到这个题目没有正确的思路
1 不知道一个整形的每一位用什么方法取(脑子短路了其实就是N%10)
看到代码随想录之后的想法
1 对输入进行计算后将值存到一个set中,通过set.contain来判断该值是否出现过,如果出现过则是无限循环,不满足快乐数的要求,如果最后为1,则满足快乐数
2 对n的计算,用一个循环,每次取n%10 的平方来加到sum中,然后n/10继续往下走,当n<0时结束
自己实现过程中遇到的困难
1 没思路,其实要看到无限循环这个关键信息
2 实现的过程中把下图的s.add(n)和n=getNumbers(n);这两句搞反了,导致结果出错,最后为1又进行了一次计算结果变成0了,不满足return n==1
class Solution {
public boolean isHappy(int n) {
//每个位置上数字的平方和
//用一个map,存放1~9每个数字的平方?没理解题意的想法
//问题在于怎么把数字拆分成每一位?每次对10取余即可
// 要注意到题目的第二点,要么变成1要么无限循环始终变不到1
//无限循环要怎么验证?考虑到去重,当出现重复的时就跳出循环 contains这个函数很关键
HashSet<Integer> set = new HashSet<Integer>();
while(n!=1&&!set.contains(n)){
//下面这两条,顺序不能反 先存入之后,再进行计算,若先计算再存入,会报错,最后为1又进行了一次计算结果变成0了
set.add(n);
n=getNumbers(n);
}
//return这个写法也很好
return n==1;
}
//这个函数获取平方和
public int getNumbers(int n){
int sum = 0;
while(n>0){
sum+= (n%10) * (n%10);
n=n/10;
}
return sum;
}
}
两数之和
建议:本题虽然是 力扣第一题,但是还是挺难的,也是 代码随想录中 数组,set之后,使用map解决哈希问题的第一题。
建议大家先看视频讲解,然后尝试自己写代码,在看文章讲解,加深印象。
题目链接/文章讲解/视频讲解:代码随想录
看到题目的第一想法
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
其实就是来判断target-当前下标的值,是否在之前的数组中出现过,但是我没想到target-值 ==出现过的旧值 这个点
没有思路,只想到了用暴力的方式来,每个数的平方相加看是否等于,然后再返回
看到代码随想录之后的想法
1,代码随想录使用了map来解决,这个题的关键在于你需要通过满足条件的值返回数组的下标,于是我们可以通过值和下标来建立一个map
2,通过 map.containsKey(target-当前值)判断 target-当前值 这个数,是否在之前的数组中出现过,如果出现过则满足题目所给,返回当前的下标和map.get(target-当前值) 的下标
自己实现过程中遇到的困难
1自己没有思路,卡哥给的思路不错,看完之后马上就会写了
2map的相关API也需要再熟悉一下 map.containsKey
class Solution {
public int[] twoSum(int[] nums, int target) {
//和为目标值的那两个整数
//如果用暴力的话,把数组中每个值加上一遍
//哈希是快速从目标数组中找到对应的值
//map 是能在最快的时间内,判断这个key是否在map里出现过
//我的思路:只想到了暴力解,没有想到用哈希能怎么优化
//卡哥的思路:遍历的时候用一个map存数组的值和下标,因为我们是要看值是否遍历过所以,key应该为值
//而target - 遍历到的当前值 - 差值,通过这个差值当作key来遍历map判断数组中是否遍历过该元素
//从而得到目标的两个下标
Map map = new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
int offset = target-nums[i];
if(map.containsKey(offset)){
return new int[]{(Integer)map.get(offset),i};
}
map.put(nums[i],i);
}
return null;
}
}