day 7 第三章 哈希表

64 阅读3分钟

题目

454.四数相加II

本题解题步骤:

  1. 首先定义 一个unordered_map,key放a和b两数之和,value 放a和b两数之和出现的次数。
  2. 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中。
  3. 定义int变量count,用来统计 a+b+c+d = 0 出现的次数。
  4. 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。
  5. 最后返回统计值 count 就可以了

Java Map操作:
map.put(x, map.getOrDefault(x,0)+1);

    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int i : nums1){
            for(int j : nums2){
                int x = i + j;
                map.put(x, map.getOrDefault(x,0)+1);
            }
        }
        int count = 0;
        for(int i : nums3){
            for(int j : nums4){
                int y = 0 - i - j;
                if(map.containsKey(y)){
                    count += map.get(y);
                }
            }
        }
        return count;
    }

383. 赎金信

和242.有效的字母异位词很像,直接ac了。题目提到只有小写字母,所以用数组当哈希表

Java操作

  1. String类型的遍历用增强for循环,先用.toCharArray()转为字符数组。
        for(char c : s.toCharArray()){  
            map.put(c,map.getOrDefault(c,0)+1);
        }
  1. map的遍历
    1. 只遍历值,用.values()for(Integer i : map.values()){}
    2. 键值对,用.keySet()for(Character c : map.keySet(){}
    public boolean canConstruct(String ransomNote, String magazine) {
        Map<Character, Integer> map = new HashMap<>();
        for(char c : ransomNote.toCharArray()){
            map.put(c,map.getOrDefault(c,0)+1);
        }
        for(char d : magazine.toCharArray()){
            map.put(d,map.getOrDefault(d,0)-1);
        }
        for(Integer i : map.values()){
            if(i > 0){
                return false;
            }
        }
        return true;
    }

15. 三数之和

难,多做几次。

双指针法,理清去重逻辑。

时间复杂度:O(n^2)。
首先排序的时间复杂度为O(NlogN);第一个循环,遍历元素i,在每个元素i中,两个指针left和right会把数组也遍历一遍,因此总共的遍历的复杂度是O(N^2),加起来就是O(N^2);

java操作
直接生成ArrayList操作 Arrays.asList(nums[i], nums[left], nums[right])

Java Arrays.asList()方法详解

    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        for(int i = 0; i < nums.length; i++){
            if(nums[i] > 0){
                return res;
            }
            if(i > 0 && nums[i] == nums[i - 1]){
                continue;
            }
            int left = i + 1;
            int right = nums.length - 1;
            while(right > left){
                int x = nums[i] + nums[left] + nums[right];
                if(x > 0){
                    right--;
                }else if(x < 0){
                    left++;
                }else{
                    res.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    while(right > left && nums[right] == nums[right - 1]){
                        right--;
                    }
                    while(right > left && nums[left] == nums[left + 1]){
                        left++;
                    }
                    right--;
                    left++;
                }
            }
        }
        return res;
    }

18. 四数之和

三数之和 进阶版,都是使用双指针法, 基本解法就是在三数之和的基础上再套一层for循环。

四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。

注意剪枝和去重!

时间复杂度:O(n^3) 其中 n是数组的长度。排序的时间复杂度是 O(n log n),枚举四元组的时间复杂度是 O(n^3),因此总时间复杂度为 O(n^3+n\log n)=O(n^3)

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        for(int i = 0; i < nums.length; i++){
            // if((long) nums[i] * 4 > target) break; 
            if(nums[i] > target && target > 0) break; //一级剪枝
            if(i > 0 && nums[i] == nums[i - 1]) continue; //一级去重
            for(int j = i + 1; j < nums.length; j++){
                // if((long) nums[j] * 3 > target - nums[i]) break;  
                if(nums[j] + nums[i] > target && target >= 0) break; //二级剪枝
                if(j > i + 1 && nums[j] == nums[j - 1]) continue;  //二级去重
                int left = j + 1;
                int right = nums.length -1;
                while(right > left){
                    long x = nums[i] + nums[j] + nums[left] + nums[right];
                    if(x > target){
                        right--;
                    }else if(x < target){
                        left++;
                    }else{
                        res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        while(right > left && nums[right] == nums[right - 1]){
                            right--;
                        }
                        while(right > left && nums[left] == nums[left + 1]){
                            left++;
                        }
                        right--;
                        left++;
                    }
                }
            }
        }
        return res;
    }
}

总结

  1. 在三数之和中,双指针法就是将原本暴力O(n^3)的解法,降为O(n^2)的解法,四数之和的双指针解法就是将原本暴力O(n^4)的解法,降为O(n^3)的解法。那么一样的道理,五数之和、六数之和等等都采用这种解法;