哈希表基础算法

本文介绍了哈希表在解决LeetCode中一些算法问题的应用,如判断字母异位词、找数组交集、快乐数检测以及两数之和等。通过哈希表的高效查找特性,优化了算法的解决方案。
摘要由CSDN通过智能技术生成

哈希表有关算法基础题

哈希表概述

本文主要内容

LeetCode242.有效的字母异位词

在这里插入图片描述

思路一:遍历字符串使用hashmap的键值key保存字母,value保存key字母出现的次数,第二个字符串,出现key值遍减一, 最后hashmap中value全为0

 public boolean isAnagram(String s, String t) {
        if(s.length()!=t.length()){         //长度不一致返回false
            return false;
        }

        HashMap<Character,Integer> map = new HashMap<>();   //创建hash表 key放字符 value放个数

        for(int i=0;i<s.length();i++){      //遍历字符串
           if(map.containsKey(s.charAt(i))){    //如果包含value+1;
               Integer value = map.get(s.charAt(i));
               value++;
               map.put(s.charAt(i),value);
           } else{                              //不包含,添加value为1
               map.put(s.charAt(i),1);
           }
            if(map.containsKey((t.charAt(i)))){ //t字符串中,如果包含value-1
                Integer value = map.get(t.charAt(i));
                value--;
                map.put(t.charAt(i),value);
            }else{                              //不包含,添加value为-1
                map.put(t.charAt(i),-1);
            }
       }


        Set<Character> characters = map.keySet();   //遍历map 每一个value都是0返回正确
        for (char c: characters) {
            if(map.get(c)!=0){
                return false;
            }
        }
        return true;

    }

思路二:类似与思路一,创建字符串数组(26)个代替hashmap该方法更适合本题,因为判断的主题为char类型,若判断类型为自定义类,可以使用hashmap

public boolean isAnagram(String s, String t) {
         if (s.length() != t.length()) {    //长度不一致返回false
            return false;
        }

        int cnt[] = new int[26];            //26个英文字母
        for(int i=0;i< s.length();i++){     //遍历两个字符串
            cnt[s.charAt(i)-97]++;          //s中字符对应的数加一
            cnt[t.charAt(i)-97]--;          //t中字符对应的数减一
        }
        for(int i=0;i< cnt.length;i++){     //遍历数组全为零正确
            if(cnt[i]!=0){
                return false;
            }
        }

        return true;

    }

LeetCode349. 两个数组的交集

在这里插入图片描述

思路:

1.将其中一个数组元素添加到hashset中(去重)本题重点是交集的元素,而不是个数
2.遍历第二个数组,判断集合中是否包含数组中的元素
3.要注意的是,不确定有多少个重合的,所以不能创建数组添加,需要通过集合转数组

public int[] intersection(int[] nums1, int[] nums2) {
         if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {    
            return new int[0];  //不满足条件
        }
        Set<Integer> set = new HashSet<>();         //创建集合1
        for(int i=0;i< nums1.length;i++){           //遍历数组一将不重复的元素添加到set
            set.add(nums1[i]);
        }
        Set<Integer> set1 = new HashSet<>();        //创建集合2
        for(int i=0;i< nums2.length;i++){           //遍历数组二将set中包含的元素添加到set1
            if(set.contains(nums2[i])){
                set1.add(nums2[i]);
            }
        }

        int []array = new int[set1.size()];        //创建数组
        Object[] objects = set1.toArray();          //将set1中整型元素添加到数组中
        for(int i=0;i<set1.size();i++){
            array[i] = (int)objects[i];
        }
        return array;
    }

LeetCode202. 快乐数

在这里插入图片描述

思路一:
每次得到一个新的数,取余数得到每一位的数,再次平方->判断。。取余。貌似要用到递归。
重审题目:提示会进入无线循环且n为int型 可以得到最大的数为1999 9999 9 其加工后为730
数字有限,所以必定存在环。所以可以用set集合判断

public boolean isHappy(int n) {
        HashSet<Integer> set = new HashSet<>(); //set集合
        while(n!=1&&!set.contains(n)){          //n不等于1 n没有出现
            set.add(n);                         //添加到集合
             n = getNextNumber(n);              //加工数据
        }       
        return n==1;                            //循环不存在时,n是否等于1
    }

     private static int getNextNumber(int n){

        int res = 0;
        while(n>0){                             //从个位取数加工
            int pow = n%10;
            res += pow*pow;
            n/=10;
        }
        

        return res;
    }

思路二:快慢指针
接思想一,加工一次数据成为一次操作,此时可以设置慢指针每次加工一次,快指针每次加工两次,当fast==slow时
1.若n为不快乐数,fast与slow在一个环形链表中走,此时fast等于slow一定不为1
2.若是快乐数,fast与slow在一条含有环,且1自成环的链路上走,此时fast等于slow必定为1

如图

public  boolean isHappy(int n) {
        int fast = n;                       //快指针
        int slow = n;                       //慢指针
        do {
            fast = getNextNumber(getNextNumber(fast));      //加工两次
            slow = getNextNumber(slow);                     //加工一次
        } while (fast != slow); 
        return slow == 1;                                   //fast==slow时返回slow==1
    }

    private  int getNextNumber(int n) {     //加工一次
        int res = 0;
        while (n > 0) {
            int pow = n % 10;
            res += pow * pow;
            n /= 10;
        }
        return res;
    }
}

补充:
快乐数
不是快乐数的数称为不快乐数(unhappy number),所有不快乐数的数位平方和计算,最後都会进入 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4 的循环中

LeetCode1. 两数之和

在这里插入图片描述

思路一:最先想到暴力解法第一层循环遍历所有元素,第二层循环找当前元素和等于target 这里我们介绍第二种思路
思路二:本质是在数组/集合中寻找元素,题目描述为在集合中寻找两个数a,b使得a+b=target可以理解为在集合中寻找target-a若存在直接返回

public int[] twoSum(int[] nums, int target) {
       int arr[] = new int[2];              //结果数组
        Map<Integer,Integer> map = new HashMap<>();   //map集合 key用来存放差值,value用来存放index
        for(int i=0;i<nums.length;i++){                 //遍历数组
            if(map.containsKey(nums[i])){               //如果map中key与num相同,说明存在和为target的两个数
                arr[0] = map.get(nums[i]);              
                arr[1] = i;
                return arr;
            }else {                                 //如果不存在 将target-nums[i]d的值添加进map
                map.put(target-nums[i],i);
            }
        }
        return null;
    }

LeetCode454. 四数相加II

在这里插入图片描述

思路:试图模仿两数之和,但四个数出自四个不同的数组,而且target没有明确给出,尝试将数组分组合并,出现条件两个数组的和是另两个数组的和的相反数
本题采用hashmap的思想,将将nums1和nums2相加并添加到map的key中,并同时记录出现次数,将3,4数组的元素两两相加,取反在map中查找,匹配并加和对应value值

本题重点 : 1:如何将数组分组 2:一组的数组如何与另一组比较 3.如何找出所有答案

public static int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer, Integer> map = new HashMap<>();    //创建hashmap
        for (int a : nums1) {                           //将nums1和nums2相加并添加到map的key中
            for (int b : nums2) {                   
                if (map.containsKey(a + b)) {           
                    map.put(a + b, map.get(a+b)+1);     //用value记录出现了多少次相同的和
                } else {
                    map.put(a + b, 1);          
                }
            }
        }

        int cnt = 0;                                    //最终计数
        for (int c : nums3) {                           //遍历nums3和nums4
            for (int d : nums4) {                       
                if (map.containsKey(-(c + d))) {        //将和取负数在集合中查找
                    cnt += map.get(-(c + d));           //查找成功,将cnt加上对应-(c+d)的value值
                }

            }
        }
        return cnt;                                     //返回
    }

LeetCode383. 赎金信

在这里插入图片描述

思路一:关键在处理ransomNote中重复的元素,建立hashmap用key值存储magazine中字符,value存储key元素重复的次数,再遍历ransomeNote,匹配则对应value减一 若不匹配或value==0 失败

public  boolean canConstruct(String ransomNote, String magazine) {

        Map<Character, Integer> map = new HashMap<>();      //创建hashmap 
        char s1[] = magazine.toCharArray();                 //转为字符数组
        for (int i = 0; i < s1.length; i++) {               
            if (map.containsKey(s1[i])) {                  //将字符数组添加进map并记录出现次数
                map.put(s1[i], map.get(s1[i]) + 1);         
            } else {                                   
                map.put(s1[i], 1);
            }
        }
        char s2[] = ransomNote.toCharArray();               //将ransomNote转为字符数组
        for (int i = 0; i < s2.length; i++) {                   
            if (!map.containsKey(s2[i])) {                  //不包含或者包含value==0返回false
                return false;
            } else if (map.get(s2[i]) == 0) {
                return false;
            } else {                                
                map.put(s2[i], map.get(s2[i]) - 1);         //匹配条件,value减一
            }
        }

        return true;                                        
    }

思路二:重审题目发现和第242题类似
本题重点:1:magazine中的元素不可以重复使用 2:都是小写 3:本质还是查找是否存在
因此我们换一种哈希做法

public static boolean canConstruct(String ransomNote, String magazine) {
        if (ransomNote.length() > magazine.length()) {      //rans大于magazine返回fasle
            return false;
        }
        int arr[] = new int[26];                            //26个小写字母
        for (char c : magazine.toCharArray()) {             //遍历maga记录每个字母出现的次数
            arr[c - 'a']++;                               
        }
        for (char c : ransomNote.toCharArray()) {           //遍历rans将每个字母对应位置数值减一
            arr[c - 'a']--;
            if (arr[c - 'a'] < 0) {                         //不包含返回false
                return false;
            }
        }
        return true;
    }

LeetCode15. 三数之和

在这里插入图片描述

思路:本题难在满足a+b+c=0条件的a,b,c在一个数组中且必须是三个不同的索引,考虑map集合时对索引的去重十分的复杂,这里在两数之和的启发下使用三指针方法解决固定一个指针:剩余两个指针遍历数组,加和为第一个指针的相反数
本题难点:1:数组排序的思想 2:三指针 3:i,j,k的去重问题

 public  List<List<Integer>> threeSum(int[] nums) {
        int last = nums.length - 1;
        List<List<Integer>> res = new ArrayList<>(last>255?256:last+1);
        Arrays.sort(nums);      //排序
       
        int j, k;
        if (nums[0] > 0 || nums[last] < 0||nums.length<3) {//和为0 第一个数大于0 最后一个数小于0 或者个数小于3
            return res;
        }   
        if (nums[0] == 0 && nums[last] == 0) {     //数组元素全为零
            res.add(Arrays.asList(0, 0, 0));
            return res;
        }
        int target;
        for (int i = 0; i <= last - 2; i++) {       //  固定i  ijk不可以重复 所以i<=last-2
            j = i + 1;                              //设置双指针      
            k = last;
            if (i > 0 && nums[i] == nums[i - 1]) {  //给i去重
                continue;
            }
            if(nums[i]>0){break;}                   //跳出特例
            while (j < k) {                         //双指针遍历
                target = nums[i] + nums[j] + nums[k];
                if (target < 0) {
                    j++;
                } else if (target > 0) {
                    k--;
                } else {    
                    res.add(Arrays.asList(nums[i], nums[j++], nums[k--]));//满足条件添加
                    while (j<k&&nums[j] == nums[j-1]) {       //j向前判重
                        j++;
                    }
                    while (k>j&&nums[k] == nums[k + 1]) {     //k向后判重
                        k--;        
                    }                                       //因为i固定 j,k任意重复,结果必定重复
                }
            }

        }
        return res;

    }

LeetCode18. 四数之和

在这里插入图片描述

与三数之和不同的时,本题加和不为0,剪枝难度加大,使用四个指针的方式:将前两个指针固定,后两个类似于双指针,代码如下

   public  List<List<Integer>> fourSum(int[] nums, int target) {
        int n = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        if (n < 4) {return res;}    //不足四个返回res
        Arrays.sort(nums);          //排序
        int i, j, k, l;             //四个指针 每次固定前两个
        long sum1_2, sum1_4;        //前两个和前四个数之和

        for (i = 0; i < n - 3; i++) {   //固定第一个数,ijkl不能重复所以i<n-3
            
            if(target>0&&nums[i]>target) return res;//剪枝 因为排完序,第一个数大于,四个数和必大于    
            
            if (i > 0 && nums[i] == nums[i - 1]) {  //将前一个相同的i过滤掉
                continue;                           //注意由于是去重不可以是i==i+1
            }
            j = i + 1;                              //固定第二个数

            for (; j < n - 2; j++) {                //n-2同理
                
                sum1_2 = nums[i] + nums[j];         //前个数相加
                //剪枝
                // if(target>0&&nums[j]>0&&sum1_2>target){
                //     break;
                // }                                  
                if (j > i + 1 && nums[j] == nums[j - 1]) {//对j向前去重
                    continue;
                }
                k = j + 1;                          //双指针从j+1到表尾开始遍历
                l = n - 1;
                

                while (k < l) {                     //相遇为跳出条件
                    sum1_4 = sum1_2 + nums[k] + nums[l];
                    if (sum1_4 < target) {          
                        k++;
                    } else if (sum1_4 > target) {
                        l--;
                    } else {//满足条件加入k++ l--
                        res.add(Arrays.asList(nums[i], nums[j], nums[k++], nums[l--]));
                        while (k < l && nums[k] == nums[k - 1]) k++;//对k l去重
                        while (k < l && nums[l] == nums[l + 1]) l--;//因为ij固定 kl任意固定答案固定
                    }
                }

            }

        }

        return res;
    }

总结

哈希表的应用多发生在对集合元素的查找,在无序集合中可以完成双指针无法完成的工作如两数之和,但有时双指针可以替换哈希表解决问题,如三数之和。在哈希与双指针之间的选择是重点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值