【代码随想录训练营】【Day07】第三章|哈希表|454.四数相加II|383. 赎金信|15. 三数之和|18. 四数之和|总结

454.四数相加II

题目详细:LeetCode.454

四数相加的解题思路很简单,其解题过程可以看作是先计算两个小的数组的两数之和,再整体计算一个大的两数之和的过程:

  • 将nums1和nums2作为组合A、nums3和nums4作为一组合B
  • 定义一个Map,用于统计组合A两数之和,各个结果的出现次数
  • 因为答案要求四数相加为0,即组合A和组合B的结果相加也应为0
  • 所以,只需要计算出组合B的两数之和的相反数在Map中出现的次数,并累计次数即可得到正确答案

Java解法(哈希表)

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int n = nums1.length, ans = 0;
        Map<Integer,Integer> map = new HashMap<>();
        for(int i=0; i < n; i++){
            for(int j=0; j < n; j++){
                int sum = nums1[i] + nums2[j];
                map.put(sum, map.getOrDefault(sum, 0) + 1);
            }
        }
        for(int k=0; k < n; k++){
            for(int l=0; l < n; l++){
                int sum =  0 - (nums3[k] + nums4[l]);
                ans += map.getOrDefault(sum, 0);
            }
        }
        return ans;
    }
}

383. 赎金信

题目详细:LeetCode.383

这道题和字母异位词的解题思路差不多,可以通过数组或者HashMap来解决,两者的解题思路是相同的:

  • 定义一个HashMap,先遍历并统计字符串magazine中每个字符能的可利用次数
  • 再遍历字符串ransom的每个字符,由于每个字符只能使用一次,还需要将HashMap中相对应的字符的可利用次数 - 1
  • 当字符的可利用次数 < 0 时,则说明字符串magazine中的字符不足以组成字符串ransom,返回false
  • 当字符串ransom顺利遍历后,返回true

Java解法(数组):

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int[] set = new int[26];
        for(char c: magazine.toCharArray()){
            set[c - 'a']++;
        }
        for(char c: ransomNote.toCharArray()){
            int f = --set[c - 'a'];
            if(f < 0){
                return false;
            }
        }
        for(int n: set){
            if(n < 0){
                return false;
            }
        }
        return true;
    }
}

Java解法(哈希表):

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        Map<Character, Integer> map = new HashMap<>();
        for(char m: magazine.toCharArray()){
            map.put(m, map.getOrDefault(m, 0) + 1);
        }
        for(char r: ransomNote.toCharArray()){
            int t = map.getOrDefault(r, 0);
            if(t <= 0){
                return false;
            }
            map.put(r, t-1);
        }
        return true;
    }
}

15. 三数之和

题目详细:LeetCode.15

这道题我一开始的思路比较简单,就是先将数组排序之后,将计算三数之和的过程看作是计算两次两数之和的过程,利用HashSet记录数字a和数字b的和,再看有没有数字c可以满足三数之和为0的要求,如果满足则作为一个三元组结果。

但我想的还是太简单了,这道题的难点不在于计算数值之和,而是在于去重,而且是要对每一个出现过的数值都进行去重操作,否则会出现重复的三元组。

在看完题解之后,发现双指针法比使用HashSet来解决问题会更加容易理解且简便:三数之和—题解

Java解法(双指针法):

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> ans = new ArrayList<>();
        for(int i = 0; i < nums.length; i++){
            // 排序之后,如果第i个元素已经大于零,那么其后续的数值都不可能凑成三数之和为0三元组
            if (nums[i] > 0) {
                break;
            }
            // 去重
            if(i > 0 && nums[i] == nums[i-1]){
                continue;
            }
            // 双指针法
            int l = i+1, r = nums.length-1;
            while(l < r){
                int sum = nums[i] + nums[l] + nums[r];
                if(sum > 0){
                    r--;
                }else if(sum < 0){
                    l++;
                }else{
                    ans.add(Arrays.asList(nums[i], nums[l], nums[r]));
                    r--;
                    l++;
                    // 去重
                    while(l < r && nums[r] == nums[r+1]) r--;
                    while(l < r && nums[l] == nums[l-1]) l++;
                }
            }
            
        }
        return ans;
    }
}

18. 四数之和

题目详细:LeetCode.18

作为练习题,这道题对我来说太难了,可以学,但是莫必要,日后再见


总结

哈希法是典型的利用空间换时间的方法,当需要判断某个数据是否记录过,或者进行去重操作时,基本都会优先到哈希法。
在做题的过程中,我发现哈希法是一个学起来简单,但是运用起来也简单,但是要用好就很难的方法,特别是在做今天《三数之和》和《四数之和》两道题目的过程中,真的就是给了我一种“梦破碎了”的感觉。
在运用Java提供给我们的哈希表容器后,我发现其底层的实现更加有趣,它是底层是用什么进行存储的,存储的结构是什么样的,如何对数据进行哈希的,如何避免哈希碰撞的等问题也值得我们去学习和探究。


今日作结诗,同时也是我对哈希法的运用由浅至深之后的感触:

坐井观天只有一孔之见,登山远望方知天外有天。

你可能感兴趣的:(代码随想录训练营,散列表,leetcode,算法)