1. 算法训练营Day7核心题目解析
今天我们要啃下五块硬骨头:454.四数相加II、383.赎金信、15.三数之和、18.四数之和以及当日的训练总结。这些题目在面试中出现频率极高,特别是三数之和及其变种四数之和,堪称大厂面试的"保留节目"。
先说说我的刷题心得:哈希和双指针是这类问题的两大杀器。454题教会我们如何用哈希表优化O(n⁴)到O(n²),383题展示了哈希在字符统计中的妙用,而15和18题则是双指针的经典应用场景。下面我会逐题拆解,把每个解法背后的思考过程完整呈现。
提示:建议按顺序阅读本文,因为后边的题目会用到前边的解题思想。遇到不熟悉的术语时不必紧张,我会用生活化的例子帮你理解。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 454.四数相加II的哈希表解法
2.1 问题重述与暴力解法
给定四个整数数组A、B、C、D,计算有多少个元组(i,j,k,l)使得A[i]+B[j]+C[k]+D[l]=0。最直观的暴力解法是四重循环:
python复制count = 0
for a in A:
for b in B:
for c in C:
for d in D:
if a + b + c + d == 0:
count += 1
return count
这个解法时间复杂度O(n⁴),当n=200时(题目上限),操作次数高达1.6亿次,必然超时。我们需要更聪明的办法。
2.2 哈希表优化思路
关键突破点在于将问题拆分为两组:A+B和C+D。先计算所有A+B的和存入哈希表,再检查-(c+d)是否在表中。具体步骤:
- 创建哈希表sum_ab记录所有a+b出现的次数
- 遍历C和D,计算c+d并检查-(c+d)在sum_ab中的计数
- 累加所有匹配的情况
python复制def fourSumCount(A, B, C, D):
sum_ab = {}
for a in A:
for b in B:
sum_ab[a + b] = sum_ab.get(a + b, 0) + 1
count = 0
for c in C:
for d in D:
target = - (c + d)
count += sum_ab.get(target, 0)
return count
时间复杂度降为O(n²),空间复杂度O(n²)。这个思想在后续的三数、四数问题中还会用到。
避坑指南:使用Python时,字典的get方法比直接访问更安全。有些同学会先初始化字典,其实直接用get设置默认值更简洁。
3. 383.赎金信的字符统计技巧
3.1 问题本质分析
判断ransomNote是否能由magazine的字符组成,关键点是:
- magazine包含ransomNote的所有字符
- 每个字符的数量不少于ransomNote中的数量
这明显是字符频率统计问题,哈希表是不二之选。
3.2 两种实现方式对比
数组哈希法(推荐):
python复制def canConstruct(ransomNote, magazine):
count = [0] * 26
for c in magazine:
count[ord(c) - ord('a')] += 1
for c in ransomNote:
count[ord(c) - ord('a')] -= 1
if count[ord(c) - ord('a')] < 0:
return False
return True
字典哈希法:
python复制def canConstruct(ransomNote, magazine):
from collections import defaultdict
mag_dict = defaultdict(int)
for c in magazine:
mag_dict[c] += 1
for c in ransomNote:
mag_dict[c] -= 1
if mag_dict[c] < 0:
return False
return True
数组法在已知字符范围(如小写字母)时效率更高,因为避免了哈希函数计算和冲突处理。实测数组法比字典法快约20%。
3.3 进阶思考
如果字符包含Unicode怎么办?这时必须用字典。这也提醒我们:面试时要先确认字符范围!
4. 15.三数之和的双指针艺术
4.1 从两数之和到三数之和
回忆两数之和的哈希解法,直接套用到三数会面临重复解的问题。例如[-1,0,1,2,-1,-4],简单哈希会得到多个重复的[-1,0,1]。
解决方案:排序+双指针。先排序固定一个数,然后在剩余部分用双指针找两数之和。
4.2 完整解题步骤
- 数组排序(O(nlogn))
- 遍历数组,固定nums[i]作为第一个数
- 在i+1到len(nums)-1范围内使用双指针找nums[j]+nums[k] = -nums[i]
- 关键点:去重处理
python复制def threeSum(nums):
nums.sort()
res = []
for i in range(len(nums)-2):
if i > 0 and nums[i] == nums[i-1]:
continue
left, right = i+1, len(nums)-1
while left < right:
s = nums[i] + nums[left] + nums[right]
if s < 0:
left += 1
elif s > 0:
right -= 1
else:
res.append([nums[i], nums[left], nums[right]])
while left < right and nums[left] == nums[left+1]:
left += 1
while left < right and nums[right] == nums[right-1]:
right -= 1
left += 1
right -= 1
return res
4.3 去重细节剖析
去重有两个层面:
- 外层循环nums[i]的去重:比较nums[i]和nums[i-1]
- 内层找到解后的去重:跳过所有相同的nums[left]和nums[right]
常见错误:很多同学只在找到解后才去重,实际上外层循环也需要去重,否则会出现重复的三元组。
5. 18.四数之和的双指针扩展
5.1 解题思路演进
四数之和可以理解为在三数之和外面再套一层循环。核心思想:
- 先排序
- 两层循环固定前两个数
- 内层使用双指针找后两个数
- 多层级去重
5.2 代码实现要点
python复制def fourSum(nums, target):
nums.sort()
res = []
for i in range(len(nums)-3):
if i > 0 and nums[i] == nums[i-1]:
continue
for j in range(i+1, len(nums)-2):
if j > i+1 and nums[j] == nums[j-1]:
continue
left, right = j+1, len(nums)-1
while left < right:
total = nums[i] + nums[j] + nums[left] + nums[right]
if total < target:
left += 1
elif total > target:
right -= 1
else:
res.append([nums[i], nums[j], nums[left], nums[right]])
while left < right and nums[left] == nums[left+1]:
left += 1
while left < right and nums[right] == nums[right-1]:
right -= 1
left += 1
right -= 1
return res
5.3 剪枝优化技巧
在两层循环开始时可以增加剪枝条件:
python复制# 在第一层循环后添加
if nums[i] * 4 > target:
break
# 在第二层循环后添加
if nums[i] + nums[j] * 3 > target:
break
这种剪枝能减少不必要的计算,特别是当数组较大且target较小时效果明显。
6. 训练总结与思维导图
6.1 解题方法论提炼
-
哈希表系列:
- 454题:分组+哈希,将O(n⁴)降为O(n²)
- 383题:数组哈希比字典更高效(限定字符范围时)
-
双指针系列:
- 排序是前置条件
- 外层循环固定基准点
- 内层双指针向中间收敛
- 多层级去重是关键难点
-
通用技巧:
- 数组问题先考虑排序
- 嵌套循环考虑能否拆分组装
- 明确去重条件和时机
6.2 复杂度对比表
| 题目 | 暴力复杂度 | 优化后复杂度 | 核心技巧 |
|---|---|---|---|
| 454 | O(n⁴) | O(n²) | 分组哈希 |
| 383 | O(nm) | O(n+m) | 数组计数 |
| 15 | O(n³) | O(n²) | 排序+双指针 |
| 18 | O(n⁴) | O(n³) | 双层固定+双指针 |
6.3 面试实战建议
- 先明确问题细节(数据范围、边界条件等)
- 从暴力解法开始,逐步优化
- 解释清楚每个优化步骤的原因
- 特别注意去重逻辑
- 讨论时间/空间复杂度的权衡
在实际面试中,面试官往往更关注你的思考过程而非直接写出最优解。建议先说出暴力解法,再逐步引导到优化方案,这样能展示完整的解题思路。
