242.有效的字母异位词
题目
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的字母异位词。
注意:若 s
和 t
中每个字符出现的次数都相同,则称 s
和 t
互为字母异位词。
代码
class Solution {
public boolean isAnagram(String s, String t) {
int[] hash = new int[26]; //字母哈希表,默认初始值为0
//先判断两个字符串的长度,长度不相同肯定不是异位词
//语法:s.length() != t.length编译出错,要有()
if(s.length() != t.length()){
return false;
}
//长度相同时,再用数组哈希判断
else{
//遍历s和t,根据字母出现情况对hash表的值进行加减
for(int i = 0;i < s.length();i++){
hash[s.charAt(i) - 'a']++; //s中的字母让hash对应值+1
hash[t.charAt(i) - 'a']--; //t中的字母让hash对应值-1
}
}
//s和t操作完hash表之后,判断hash表的值是否全为0
for(int i = 0;i < hash.length;i++){
if(hash[i] != 0){
return false; //只要出现一个不是0的情况就说明字母不一样
}
}
return true;
}
}
总结
1.原理
(1)核心判断原则:判断s和t字母异位次,就是要求字母只能出现顺序不一样,字母的类型和对应的出现次数都要一致。
(2)为什么想到用哈希表:哈希表主要用于判断某个元素在集合中是否出现or存在。而这里判断s和t的字母是否一样(类型和次数),类型是否相同相当于在hash表中判断字母是否出现,次数是否一样可以用hash值的加减来控制。
(3)为什么用数组哈希:使用数组哈希的条件是hash表的长度受限,且hash值是连续的。这里s和t都是小写字母,只有26情况,满足长度受限的条件。其次字母的ASCII值是连续的,a-z正好对应数组索引的0-25,,也满足连续的条件。
(4)算法流程:首先判断s和t的长度是否一样,如果长度不一样肯定不是异位词直接返回false。如果长度一样,再使用数组hash。首先。同时遍历s和t,s中的字母让hash对应值+1,t中的字母让hash对应值-1。如果s和t是异位次,遍历结束hash表应该全为0,。然后我们再遍历hash表,如果存在不为0的情况,说明不是异位次,直接返回false。最后,长度也满足条件且hash都是0,就返回true。
2.语法错误
(1)String类型的字符串长度s.length()有括号,int数组的长度hash.length没有括号。
(2)获取String类型的第i个索引值,不能用s[i],要用s.charAt(i),然后让字母-‘a’对应的是该字母在hash表中的索引,hash[s.charAt(i) - 'a']。
第二次刷题总结
1.其他都没问题,不过遍历s和t修改hash数组时,可以合并为一个for循环直接++--,简化代码。
349.两个数组的交集
题目
给定两个数组 nums1
和 nums2
,返回 它们的 交集。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序
代码
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
//错误写法:Set<Integer> set = new Hashset<>(); 语法是HashSet不是Hashset
Set<Integer> set = new HashSet<>(); //存储nums1的hash表
Set<Integer> result = new HashSet<>(); //结果唯一,用set
//遍历nums1,将其转为无序无重复的set作为哈希表
for(int i: nums1){
set.add(i);
}
//遍历num2,在set中找其元素是否存在
for(int i:nums2){
if(set.contains(i)){
result.add(i); //存在说明是交集,加入result
}
}
//result要由set转为int数组返回
//错误写法:int[] res = new int[result.length()]; //set的长度函数用size()
int[] res = new int[result.size()];
int j = 0;
for(int i:result){ //遍历set结果集
res[j++] = i;
}
return res;
}
}
总结
1.思想
(1)为什么用哈希表:哈希表用于判断一个元素在集合中是否出现过。而判断两个数组集合的交集(无重复),其实就是判断num2的元素在num1对应的hash表中是否出现过。
(2)为什么用set哈希:set用于长度不受限的且只有一个key要存储的情况。这里num1和num2中的数值取址范围很大,所以长度不受限。而返回的只是交集,而不需要返回每个交集出现的次数,所以用set就够了。
(3)算法流程:首先变量nums1,将其转为hashset结构set。然后遍历nums,判断num2中的每一个元素在set中是否出现,如果出现就说明是交集,要把这个元素加入result结果集。最后,由于函数的返回值是数组,还要把set类型的result转为int再返回。
2.注意点
result返回的是无重复的不定长的集合,所有可以确定result选用hashset存储结构。
3.语法问题:
(1)new HashSet<>():这里的Set是大写的
(2)如何判断set中元素i是否存在:set.contains(i)
(3)如何在set中加入元素i:set.add(i)
(4)如何计算set的长度:set.size(),特别注意不是length()
第二次刷题总结
1.因为集合长度和大小受限,因此也可以用哈希数组,下面附上hash数组的代码。
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
int[] hash1 = new int[1005];
int[] hash2 = new int[1005];
List<Integer> result = new ArrayList<>();
for(int i : nums1){
hash1[i]++;
}
for(int i : nums2){
hash2[i]++;
}
//错误写法:int i=0;i < nums1.length;i++
for(int i=0;i < 1005;i++){
if(hash1[i] > 0 && hash2[i] > 0){
result.add(i);
}
}
int index = 0;
int[] res = new int[result.size()];
for(int i : result){
res[index++] = i;
}
return res;
}
}
2.用hash数组存在如下问题
遍历两个hash数组判断是不是元素同时大于0,不能用nums1.length或者nums2.length,这个公式计算的是数组中真实存了多少个元素,而不是开辟的数组空间。
对于ArrayList计算长度,用的是size()函数。
3.用set哈希一次通过啦,哈哈哈哈!
202.快乐数
题目
编写一个算法来判断一个数 n
是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n
是 快乐数 就返回 true
;不是,则返回 false
。
代码
class Solution {
public boolean isHappy(int n) {
Set<Integer> set = new HashSet<>();
int sum;
while(true){
sum = 0; //每次迭代n计算平方和之前,sum要归零
while(n > 0){
int tmp = n % 10; //获取当前n的最低位值
sum += (tmp * tmp); //sum是每一位数的平方和
n /= 10; //n去掉最低位
}
if(sum == 1){
return true; //结果出现1就是快乐数
}
else if(set.contains(sum)){
return false; //结果在set中找到了,说明重复了不是快乐数
}
else{
n = sum; //更新n是上一轮计算的sum平方和
set.add(sum); //把sum加入hashset
}
}
}
}
总结
1.原理
(1)为什么用哈希表:快乐数的判断条件是如果sum平方和==1,就是快乐数,如果sum平方和开始重复迭代就不是快乐数。所以核心是要判断当前的sum是否在之前的sum集合中出现过。
(2)为什么用set哈希:sum的个数无限,且sum的取值也无限
(3)算法流程:进入一个数n,计算其平方和sum。如果sum==1,就是快乐数直接返回true。如果sum在之前的set中出现过,说明开始重复迭代了,肯定不是快乐数返回false。如果sum既不是1也没有出现过,就需要把sum加入set哈希表中,同时将n修改为当前的sum继续迭代。
2.算法错误
每次n进入while(n > 0)计算平方和之前,需要先sum=0归零,不然sum就会一直累加,运行会超时。
第二次刷题总结
1.其他都ok,还是老问题,每次计算n的平方和时,在进入while循环前,sum要归零。写完代码还是要再仔细检查一下。
1.两数之和
题目
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
代码
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map = new HashMap<>(); //保存已经遍历过的元素及其下标
int[] res = new int[2]; //保存结果下标
for(int i = 0;i < nums.length;i++){
//tmp表示对应nums[i]的目标值,已知nums[i],关键是要找tmp在map中是否存在
int tmp = target - nums[i];
//错误语法:map.containskey(tmp),没大写
if(map.containsKey(tmp)){
res[1] = i; //当前遍历元素的索引(更大的索引)
res[0] = map.get(tmp); //map中目标值的索引(更小的索引)
}
else{
map.put(nums[i], i); //如果没找到,就把遍历元素及其下标存入map
}
}
return res; //不要忘记返回下标索引数组
}
}
总结
1.原理
(1)为什么用hash表:为了找一个数组中元素之和=target的两个元素,本质就是进行值=targer的元素配对,通过遍历nums后,在hash表中寻找是否存在满足和为targer的元素。
(2)为什么用map哈希表:因为已经遍历过的元素都存在map中,其长度不受限,而且最后需要返回元素的下标,因此要把index当做value进行存储。
(3)算法流程:首先创建一个空的hashmap,表示已经遍历过的元素及其下标。然后遍历nums的每一个元素,计算对应的tmp,在map中找是否存在tmp。如果有tmp,说明配对成功,可以返回下标。如果没有tmp,配对失败,需要把当前元素和下标继续加入map。
(4)下标数组res有大小之分:res[0]是map中找到的tmp下标,因为它是之前已经访问过的元素,下标更小,res[1]是当前num[i]的i下标,下标更大。
2.语法问题
(1)map判断是否存在key:map.containsKey(key),Key要大写
(2)map中获取key对应的value:map.get(key)
(3)map中加入新元组:map.put(key,value),不是add。
3.注意点
最后不要忘记返回res数组。
第二次刷题总结
1.有两个语法错误,判断有无元素map.containsKey(key)和添加新元组map.put(key,value)错了
2.核心还是要是理解,map存储的是已经访问过的元素和他的下标。
454.四数相加II
题目
给你四个整数数组 nums1
、nums2
、nums3
和 nums4
,数组长度都是 n
,请你计算有多少个元组 (i, j, k, l)
能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
代码
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer,Integer> map = new HashMap<>(); //保存a+b的和及其出现次数
int count = 0;
//遍历数组1和2
for(int i:nums1){
for(int j:nums2){
//错误写法:int tmp = nums1[i] + nums2[j];
int tmp = i + j; //计算a+b
if(map.containsKey(tmp)){
map.put(tmp,map.get(tmp) + 1); //如果已经存在a+b,value++
}
else{
map.put(tmp,1); //如果不存在a+b,添加到map
}
//简写:map.getOrDefault(tmp,0)表示如果map中有tmp返回其value,没有tmp返回0。
//map.put(tmp,map.getOrDefault(tmp,0) + 1);
}
}
//遍历数组3和4
for(int i:nums3){
for(int j:nums4){
int tmp = i + j; //计算c+d;
if(map.containsKey(-tmp)){
count += map.get(-tmp); //如果存在-tmp,说明找到和为0了
}
//简写:map.getOrDefault(-tmp,0)表示如果map中有-tmp就返回value,没有就返回0
//count += map.getOrDefault(-tmp,0);
}
}
return count;
}
}
总结
1.思想
(1)为什么用哈希表:计算4数之后为0,可以转换为先计算a+b的哈希表map,再遍历c+d的所有情况,再在map中判断-(c+d)是否出现,本质还是判断某个元素在集合中是否出现。
(2)为什么用Map哈希表:首先a+b的和的情况是无限的,其次,函数需要返回和为0的总情况次数,而不是单纯判断是否出现总和=0,因此要用index保存a+b的等于某个值的出现次数,当遍历到c+d时,如果在map中找到-(c+d),count就加其的次数就行。
(3)算法流程:首先遍历数组A和B,计算所有a+b之和的情况,如果map中不存在就put(a+b,1),如果map中已经存在,就put(a+b,map.get(a+b)+1),把a+b的所有情况和出现次数存入map。然后遍历数组C和D,在map中判断是否存在key等于-(c+d)的,如果有,说明找到四数之和=0的情况了,把count加上就行。
2.语法错误
因为数组用了增强for遍历,计算a+b时,int tmp = nums1[i] + nums2[j]是错误写法,直接用int tmp = i + j就行。
3.语法简写
map.getOrDefault(tmp,0)表示如果map中有tmp返回其value,没有tmp返回0。所以可以用map.put(tmp,map.getOrDefault(tmp,0) + 1)取代if和else判断的情况分两种put。
第二次刷题总结
一次通过啦,nice!