1. 算法训练营第六天核心题目解析
今天我们要啃下四道经典算法题:242题有效的字母异位词、349题两个数组的交集、202题快乐数,以及1题两数之和。这几道题看似简单,但涵盖了哈希表这一重要数据结构的多种应用场景,是算法入门必刷的题目组合。
作为过来人,我建议新手按这个顺序刷题:先解决字母异位词(最基础),然后数组交集(理解集合操作),接着快乐数(循环检测技巧),最后挑战两数之和(哈希表最优解)。每道题都有独特的解题思路和陷阱,下面我会结合自己刷题时踩过的坑,详细拆解每道题的解法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 242. 有效的字母异位词
2.1 题目本质理解
字母异位词(Anagram)是指由相同字母重新排列形成的不同单词,比如"listen"和"silent"。题目要求判断两个字符串是否满足这种关系。看似简单,但有几个边界条件需要注意:
- 空字符串如何处理
- 大小写是否敏感(通常测试用例不敏感)
- Unicode字符集支持
2.2 哈希表计数法(最优解)
python复制def isAnagram(s: str, t: str) -> bool:
if len(s) != len(t):
return False
count = [0] * 26
for char in s:
count[ord(char) - ord('a')] += 1
for char in t:
count[ord(char) - ord('a')] -= 1
if count[ord(char) - ord('a')] < 0:
return False
return True
这个解法时间复杂度O(n),空间复杂度O(1)(因为字母表大小固定)。关键点:
- 先比较长度,快速失败
- 使用数组模拟哈希表(比直接用字典更快)
- 第二个循环中实时检查,提前返回
注意:如果题目明确说明只包含小写字母,才能用这种固定大小的数组。如果是Unicode字符,应该使用普通字典。
2.3 排序法对比
虽然排序后比较的方法代码更简洁(return sorted(s) == sorted(t)),但时间复杂度是O(nlogn),在面试中不建议作为主要解法。
3. 349. 两个数组的交集
3.1 问题变形与要求
这道题要求找出两个数组中都存在的元素,且结果中每个元素只能出现一次。注意与"求共同元素出现次数"问题的区别。
3.2 集合操作解法
python复制def intersection(nums1, nums2):
set1 = set(nums1)
set2 = set(nums2)
return list(set1 & set2)
这个解法利用了Python集合的交集操作,简洁高效。时间复杂度O(m+n),因为集合的构建和查询都是O(1)。
3.3 手动实现哈希表
如果不用语言内置的集合,可以这样实现:
python复制def intersection(nums1, nums2):
hash_map = {}
result = []
for num in nums1:
hash_map[num] = True
for num in nums2:
if num in hash_map and hash_map[num]:
result.append(num)
hash_map[num] = False # 避免重复添加
return result
这种写法虽然长一些,但展示了哈希表的核心思想,在面试中更能体现你的理解深度。
4. 202. 快乐数
4.1 问题定义解析
快乐数的定义是:对于一个正整数,每次将该数替换为它每个位置上的数字的平方和,然后重复这个过程,如果最终能得到1,就是快乐数。
这个问题的难点在于如何检测无限循环。我的第一个实现就因为没有处理循环情况而陷入了死循环。
4.2 哈希表检测循环
python复制def isHappy(n: int) -> bool:
seen = set()
while n != 1:
n = sum(int(d) ** 2 for d in str(n))
if n in seen:
return False
seen.add(n)
return True
关键点:
- 使用集合记录已经出现过的数字
- 一旦发现重复立即返回False
- 数字转字符串再转数字的技巧简化了数位分离
4.3 数学优化(无需哈希表)
其实快乐数有个数学性质:所有不快乐的数最终都会进入4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4的循环。利用这个性质可以优化空间复杂度:
python复制def isHappy(n: int) -> bool:
while n != 1 and n != 4:
n = sum(int(d) ** 2 for d in str(n))
return n == 1
5. 1. 两数之和
5.1 暴力法与哈希表法对比
这道题是LeetCode第一题,但并不意味着它简单。暴力解法双重循环O(n²)很容易想到,但面试官期待的是哈希表解法。
5.2 哈希表一次遍历法
python复制def twoSum(nums, target):
hash_map = {}
for i, num in enumerate(nums):
complement = target - num
if complement in hash_map:
return [hash_map[complement], i]
hash_map[num] = i
return []
这个解法精妙之处在于:
- 只需要一次遍历,时间复杂度O(n)
- 在遍历的同时构建哈希表,边建边查
- 存储的是数值到索引的映射
常见错误:先构建完整哈希表再查找,这样无法处理重复元素的情况(比如[3,3], target=6)
5.3 处理重复元素的技巧
如果题目要求所有可能的解(而不仅是第一个),可以这样修改:
python复制def twoSumAll(nums, target):
hash_map = {}
result = []
for i, num in enumerate(nums):
complement = target - num
if complement in hash_map:
for j in hash_map[complement]:
result.append([j, i])
if num not in hash_map:
hash_map[num] = []
hash_map[num].append(i)
return result
6. 哈希表应用总结与技巧
6.1 何时使用哈希表
- 需要快速查找元素是否存在(O(1)时间复杂度)
- 需要记录元素出现次数或位置信息
- 数据范围很大但实际存储的键值不多(空间优化)
6.2 Python中三种实现方式对比
- 字典(dict):最通用,支持任意可哈希类型
- 集合(set):只需要存储键时使用
- 列表模拟:当键是有限范围内的整数时最高效
6.3 常见错误与调试技巧
- 忘记处理空输入或边界条件
- 哈希表键的选择不当(比如用可变对象作为键)
- 并发修改问题(遍历时修改哈希表)
- 哈希冲突处理不当(在自定义哈希函数时)
我在实际刷题中发现,合理使用哈希表可以将很多O(n²)的问题优化到O(n),这是算法效率提升的关键一步。建议初学者先把这四道题反复练习,理解哈希表在不同场景下的应用模式。
