哈希表
问题
Java 中哈希表相关的常见算法题有哪些?
答案
哈希表(HashMap/HashSet)是面试最高频的数据结构之一,核心思想是用空间换时间,将 查找降为 。
两数之和(LeetCode 1)
HashMap 一次遍历
public int[] twoSum(int[] nums, int target) {
// key: 数值, value: 索引
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[]{map.get(complement), i};
}
map.put(nums[i], i);
}
return new int[0];
}
字母异位词分组(LeetCode 49)
排序后作为 key 分组
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<>();
for (String s : strs) {
char[] arr = s.toCharArray();
Arrays.sort(arr);
String key = new String(arr); // 排序后的字符串作为 key
map.computeIfAbsent(key, k -> new ArrayList<>()).add(s);
}
return new ArrayList<>(map.values());
}
最长连续序列(LeetCode 128)
HashSet 去重 + 向右扩展
public int longestConsecutive(int[] nums) {
Set<Integer> set = new HashSet<>();
for (int num : nums) set.add(num);
int maxLen = 0;
for (int num : set) {
// 只从序列起点开始计数(前一个数不在集合中)
if (!set.contains(num - 1)) {
int cur = num, len = 1;
while (set.contains(cur + 1)) {
cur++;
len++;
}
maxLen = Math.max(maxLen, len);
}
}
return maxLen; // 时间 O(n)
}
常见哈希表技巧
| 技巧 | 说明 | 典型题目 |
|---|---|---|
| 频次计数 | 统计字符/数字出现次数 | 字母异位词、前 K 高频元素 |
| 前缀和 + HashMap | 记录前缀和首次出现位置 | 和为 K 的子数组 |
| 双指针 + HashSet | 去重判断 | 无重复字符的最长子串 |
| 映射关系 | 建立一对一映射 | 同构字符串、单词规律 |
和为 K 的子数组(LeetCode 560)
前缀和 + HashMap
public int subarraySum(int[] nums, int k) {
// key: 前缀和, value: 出现次数
Map<Integer, Integer> prefixCount = new HashMap<>();
prefixCount.put(0, 1); // 前缀和为 0 出现 1 次
int sum = 0, count = 0;
for (int num : nums) {
sum += num;
// 如果存在前缀和 = sum - k,说明中间这段子数组和为 k
count += prefixCount.getOrDefault(sum - k, 0);
prefixCount.merge(sum, 1, Integer::sum);
}
return count;
}
常见面试问题
Q1: HashMap 和 HashSet 的区别?
答案:
HashMap存键值对<K, V>,HashSet只存键(底层就是HashMap<E, Object>)- 面试算法中,需要记录映射关系用
HashMap,只需判断存在性用HashSet
Q2: 哈希冲突如何处理?
答案:
详见 HashMap 源码解析。JDK 8 使用链地址法:数组 + 链表 + 红黑树,链表长度 > 8 且数组长度 ≥ 64 时转红黑树。
Q3: 前缀和 + HashMap 的核心思路?
答案:
遍历数组维护前缀和 sum,用 HashMap 记录每个前缀和出现的次数。当前前缀和为 sum,如果之前存在前缀和 sum - k,说明中间一段子数组的和为 k。时间 ,空间 。