【40讲系列4】哈希表

一、理论

Hash冲突及解决:关键字不同的元素映射到同样地址时会产生hash冲突,解决方法有:开放定址法(探测序列)、再哈希法、链地址法。

HashMap、HashSet-------->哈希表的查找、插入、删除的平均复杂度为O(1),

TreeMap、TreeSet-------->二叉搜索树的查找、插入、删除的平均复杂度为O(logn),但有序。

二、典型例题

①:判断有效的字母异位词(LC242)

方法1:按字典序排序——O(nlogn)

方法2:哈希表——O(n)

由于仅字符串仅包含小写字母,因此可以用数组实现简易哈希表。

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) return false;
        // 方法1:排序
        /*
        char[] chars1 = s.toCharArray();
        Arrays.sort(chars1);
        char[] chars2 = t.toCharArray();
        Arrays.sort(chars2);
        return Arrays.equals(chars1,chars2);// 注意此方法哦
        */

        // 方法2:哈希表
        // -------写法1
        /*int[] counts = new int[26];
        for (int i = 0; i < s.length(); i++) {
            counts[s.charAt(i) - 'a']++;
            counts[t.charAt(i) - 'a']--;
        }
        for (int count : counts){
            if (count != 0){
                return false;
            }
        }
        return true;*/
        // ---------写法2
        int[] counts = new int[26];
        for (char c : s.toCharArray()){
            counts[c - 'a']++;
        }
        for (char c : t.toCharArray()){
            counts[c - 'a']--;
            if (counts[c - 'a'] < 0){
                return false;
            }
        }
        return true;
    }
}

②:找出数组中和为目标值的两个数(LC01)、三个数(LC15)、四个数(LC18)

-------->两数之和

class Solution {
    // 两数之和
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer,Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(target - nums[i])){
                return new int[]{map.get(target - nums[i]),i};
            }
            map.put(nums[i],i);
        }
        return new int[0];
    }
}

-------->三数之和

方法1:使用哈希表。类似于两数之和,a = - (b+c),分别枚举b和c,查询 - (b+c)是否在Set中存在。 相当于省了一层循环,时间复杂度为O(n^2)。

(不好实现。。。)

☆☆☆☆方法2:先排序,再双指针查找。 需要注意去重!

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        if (nums == null || nums.length < 3) return res;
        Arrays.sort(nums); // 排序

        for (int i = 0; i < nums.length - 2; i++) {
            if (nums[i] > 0) break; // 如果当前数组大于0,则三数之和一定大于0
            if (i > 0 && nums[i] == nums[i-1]) continue; // 去重
            int start = i+1;
            int end = nums.length - 1;
            while (start < end){
                int tempSum = nums[i] + nums[start] + nums[end];
                if (tempSum > 0){
                    end--;
                }else if (tempSum < 0){
                    start++;
                }else{
//                    ArrayList<Integer> list = new ArrayList<>();
//                    list.add(nums[i]);
//                    list.add(nums[start]);
//                    list.add(nums[end]);
//                    res.add(list);
                    res.add(Arrays.asList(nums[i],nums[start],nums[end]));
                    while (start < end && nums[end] == nums[end-1]) end--;       // 去重
                    while (start < end && nums[start] == nums[start+1]) start++; // 去重
                    start++;
                    end--;
                }
            }
        }
        return res;
    }
}

--------->四数之和

M,以后补上

三、扩展例题—查找表相关问题

第一组(set和map的使用):LeetCode349、350. 两个数组的交集LeetCode136. 只出现一次的数字LeetCode242. 有效的字母异位词LeetCode202. 快乐数LeetCode290. 单词规律LeetCode205. 同构字符串LeetCode451. 根据字符出现频率排序

第二组(经典问题N数之和):LeetCode1. 两数之和LeetCode15. 三数之和LeetCode18. 四数之和LeetCode16. 最接近的三数之和

第三组(灵活选择键值):LeetCode454. 四数相加 IILeetCode49. 字母异位词分组

第四组(灵活选择键值):LeetCode447. 回旋镖的数量LeetCode149. 直线上最多的点数LeetCode719. 找出第 k 小的距离对

第五组(查找表+滑动窗口):LeetCode219. 存在重复元素 II

第六组(有序查找表):LeetCode220. 存在重复元素 III

原文地址:https://www.cnblogs.com/HuangYJ/p/14012005.html