代码随想录:哈希表2-6

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

题目

        给你四个整数数组 nums1nums2nums3 和 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!

  • 29
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

守岁白驹hh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值