哈希表 Hash Table
代码随想录-总汇 中包含哈希表的基础知识
题目 242. 有效的字母异位词
1 2 3 4 5 6 7 8 9 10 11 12 public boolean isAnagram (String s, String t) { if (s.length() != t.length()) return false ; int [] alpha = new int [26 ]; for (int i = 0 ; i< s.length(); i++) { alpha[s.charAt(i) - 'a' ] ++; alpha[t.charAt(i) - 'a' ] --; } for (int i=0 ;i<26 ;i++) if (alpha[i] != 0 ) return false ; return true ; }
349. 两个数组的交集
没有什么难点,但是有个用法可以学一下: Java 的 stream: 把set中的值变成array:
1 int [] res = resSet.stream().mapToInt(x -> x).toArray();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public int [] intersection(int [] nums1, int [] nums2) { Set<Integer> resSet = new HashSet <>(); Set<Integer> numSet = new HashSet <>(); for (int i : nums1) { numSet.add(i); } for (int i : nums2) { if (numSet.contains(i)) { resSet.add(i); } } int [] res = resSet.stream().mapToInt(x -> x).toArray(); return res; }
202. 快乐数
这道题主要在于识别题干中的 无限循环 , 即会出现sum重复出现 的情况 于是题目就变成:如何快速查找sum是否已经出现过 ,即哈希应用题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public boolean isHappy (int n) { Set<Integer> sumSet = new HashSet <>(); int sum = 0 ; int nCopy = n; while (!sumSet.contains(sum)) { int newN = nCopy; sumSet.add(sum); sum = 0 ; while (newN > 0 ) { sum += (newN % 10 ) * (newN % 10 ); newN /= 10 ; } nCopy = sum; if (sum == 1 ) return true ; } return false ; }
1. 两数之和
缅怀我逝去的青春 😋
1 2 3 4 5 6 7 8 9 10 11 12 13 public int [] twoSum(int [] nums, int target) { Map<Integer, Integer> val2Idx = new HashMap <>(); for (int i = 0 ; i < nums.length; i++) { int cur = nums[i]; int needNum = target - cur; if (val2Idx.containsKey(needNum)) { return new int [] {val2Idx.get(needNum), i}; } else { val2Idx.put(nums[i], i); } } return new int [] {-1 , -1 }; }
454. 四数相加 II
首先非常丑的暴力做法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private int brutalForce (int [] nums1, int [] nums2, int [] nums3, int [] nums4) { int count = 0 ; for (int i = 0 ; i < nums1.length; i++) { for (int j = 0 ; j < nums2.length; j++) { for (int k = 0 ; k < nums3.length; k++) { for (int l = 0 ; l < nums4.length; l++) { if (nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0 ) count++; } } } } return count; }
可以考虑用时间换空间,借用两数之和的思想我们可以想到哈希表:
使用哈希表来优化至 O(N^2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public int fourSumCount (int [] nums1, int [] nums2, int [] nums3, int [] nums4) { Map<Integer, Integer> sum2Count = new HashMap <>(); for (int i = 0 ; i < nums1.length; i++) { for (int j = 0 ; j < nums2.length; j++) { int sum = nums1[i] + nums2[j]; sum2Count.put(sum, sum2Count.getOrDefault(sum, 0 ) + 1 ); } } int count = 0 ; for (int k = 0 ; k < nums3.length; k++) { for (int l = 0 ; l < nums4.length; l++) { int sum = nums3[k] + nums4[l]; if (sum2Count.containsKey(-sum)) { count += sum2Count.get(-sum); } } } return count; }
383. 赎金信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public boolean canConstruct (String ransomNote, String magazine) { int [] hm = new int [26 ]; for (char r : ransomNote.toCharArray()) { int pos = r - 'a' ; hm[pos] += 1 ; } for (char m : magazine.toCharArray()) { int pos = m - 'a' ; hm[pos] -= 1 ; } for (int j : hm) { if (j > 0 ) return false ; } return true ; }
15. 三数之和
写做三数之和但是用哈希表来做会非常麻烦由于涉及到去重的操作。因此双指针会简便许多。具体流程如下:
另外一些关于去重问题的考虑:
我们有三个数需要去重 nums[i] nums[left] nums[right]:
i 的去重
在 i 入口处就可以跳过:那么问题是我们应该用 nums[i] == nums[i + 1] 还是 nums[i] == nums[i - 1]
答案是用 nums[i] == nums[i - 1]
考虑用{-1, -1 ,2} 这组数据,如果用nums[i] == nums[i + 1] 当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据就pass了。
nums[left] nums[right] 的 去重:
见代码 4. 处
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public List<List<Integer>> threeSum (int [] nums) { Arrays.sort(nums); List<List<Integer>> res = new ArrayList <>(); for (int i = 0 ; i < nums.length - 2 ; i++) { if (i > 0 && nums[i] == nums[i - 1 ]) continue ; int left = i + 1 , right = nums.length - 1 ; int num = nums[i]; int needNum = -num; while (left < right) { int currentSum = nums[left] + nums[right]; if (currentSum < needNum) { left++; } else if (currentSum > needNum) { right--; } else { List<Integer> temp = new ArrayList <>(Arrays.asList( nums[i], nums[left], nums[right] )); res.add(temp); while (left < right && nums[left] == nums[left + 1 ]) left++; while (left < right && nums[right] == nums[right - 1 ]) right--; left++; right--; } } } return res; }
18. 四数之和
本质上还是三数之和即排序加双指针但是有两个地方需要注意一下看下面的代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public List<List<Integer>> fourSum (int [] nums, int target) { Arrays.sort(nums); List<List<Integer>> res = new ArrayList <>(); for (int i = 0 ; i < nums.length - 3 ; i++) { if (i > 0 && nums[i] == nums[i - 1 ]) continue ; for (int j = i + 1 ; j < nums.length - 2 ; j++) { if (j > i + 1 && nums[j] == nums[j - 1 ]) continue ; int left = j + 1 , right = nums.length - 1 ; long sumIJ = nums[i] + nums[j]; long needSum = target - sumIJ; while (left < right) { long curSum = nums[left] + nums[right]; if (curSum < needSum) { left++; } else if (curSum > needSum) { right--; } else { List<Integer> temp = new ArrayList <>( Arrays.asList(nums[i], nums[j], nums[left], nums[right]) ); res.add(temp); while (left < right && nums[left] == nums[left + 1 ]) left++; while (left < right && nums[right] == nums[right - 1 ]) right--; left++; right--; } } } } return res; }