【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. 四数相加 II、LeetCode49. 字母异位词分组
第四组(灵活选择键值):LeetCode447. 回旋镖的数量、LeetCode149. 直线上最多的点数、LeetCode719. 找出第 k 小的距离对
第五组(查找表+滑动窗口):LeetCode219. 存在重复元素 II
第六组(有序查找表):LeetCode220. 存在重复元素 III