1. 算法训练营实战:哈希与双指针经典题解
今天继续我们的算法训练营第七天打卡,集中攻克四道经典题目:454.四数相加II、383.赎金信、15.三数之和、18.四数之和。这几道题涵盖了哈希表和双指针两大核心解题技巧,是面试中高频出现的题型。我将结合自己的刷题经验,带大家逐题拆解最优解法。
提示:建议先自行尝试解题再看解析,效果更佳。本文代码示例均基于Python3实现。
2. 454. 四数相加 II:哈希表的降维打击
2.1 问题重述
给定四个整数数组nums1、nums2、nums3、nums4,计算有多少个元组(i,j,k,l)满足:
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
2.2 暴力解法分析
最直观的解法是四重循环枚举所有组合:
python复制count = 0
for i in nums1:
for j in nums2:
for k in nums3:
for l in nums4:
if i + j + k + l == 0:
count += 1
时间复杂度O(n^4),当n=200时运算量高达1.6亿次,显然不可行。
2.3 哈希表优化思路
核心观察:可以将四数之和转化为两组两数之和的问题:
- 先计算nums1和nums2所有元素的两两之和,存入哈希表记录出现次数
- 再计算nums3和nums4所有元素的两两之和,查找哈希表中是否存在相反数
python复制def fourSumCount(nums1, nums2, nums3, nums4):
from collections import defaultdict
hashmap = defaultdict(int)
count = 0
# 计算nums1和nums2的两数之和
for n1 in nums1:
for n2 in nums2:
hashmap[n1 + n2] += 1
# 计算nums3和nums4的两数之和
for n3 in nums3:
for n4 in nums4:
target = -(n3 + n4)
if target in hashmap:
count += hashmap[target]
return count
时间复杂度降为O(n^2),空间复杂度O(n^2)。
2.4 实战技巧
- 使用defaultdict避免键不存在的判断
- 先处理较小的数组对可以节省空间
- 实际测试时,Python的Counter比defaultdict稍快
3. 383. 赎金信:字符频率统计
3.1 问题描述
判断ransomNote是否能由magazine中的字符构成(每个字符只能用一次)。
3.2 解法对比
方法一:哈希表计数
python复制def canConstruct(ransomNote, magazine):
from collections import defaultdict
count = defaultdict(int)
for c in magazine:
count[c] += 1
for c in ransomNote:
count[c] -= 1
if count[c] < 0:
return False
return True
方法二:数组计数(更高效)
python复制def canConstruct(ransomNote, magazine):
count = [0] * 26
for c in magazine:
count[ord(c) - ord('a')] += 1
for c in ransomNote:
idx = ord(c) - ord('a')
count[idx] -= 1
if count[idx] < 0:
return False
return True
3.3 性能分析
- 时间复杂度:O(m+n)
- 空间复杂度:O(1)(固定大小的数组)
- 数组法比哈希表快约30%,推荐在已知字符范围时使用
4. 15. 三数之和:双指针经典应用
4.1 问题难点
找出nums中所有不重复的三元组[a,b,c]满足a+b+c=0。
4.2 解题步骤
- 排序数组(O(nlogn))
- 固定第一个数nums[i],转化为两数之和问题
- 使用双指针left和right在剩余区间查找
python复制def threeSum(nums):
nums.sort()
res = []
n = len(nums)
for i in range(n - 2):
if i > 0 and nums[i] == nums[i-1]:
continue
left, right = i + 1, n - 1
while left < right:
total = nums[i] + nums[left] + nums[right]
if total < 0:
left += 1
elif total > 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[left]/nums[right]
- 提前终止:当nums[i] > 0时可提前结束
- 边界条件:数组长度不足3时的处理
5. 18. 四数之和:三数之和的扩展
5.1 解题思路
在三数之和基础上增加一层循环:
- 排序数组
- 固定nums[i]和nums[j]
- 在剩余区间使用双指针查找
python复制def fourSum(nums, target):
nums.sort()
res = []
n = len(nums)
for i in range(n - 3):
if i > 0 and nums[i] == nums[i-1]:
continue
for j in range(i + 1, n - 2):
if j > i + 1 and nums[j] == nums[j-1]:
continue
left, right = j + 1, n - 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.2 优化技巧
- 提前计算最小和与最大和进行剪枝
- 使用递归思路可扩展到N数之和
- 注意Python中整数溢出的处理(本题约束下无需考虑)
6. 总结与刷题建议
通过这四道题目,我们系统性地掌握了:
- 哈希表在求和问题中的降维作用
- 双指针在有序数组中的高效搜索
- 去重处理的多种实现方式
刷题时建议:
- 先手写伪代码理清思路
- 注意边界条件的测试用例
- 对比不同解法的时空复杂度
- 定期复习经典题目的变种
我在实际刷题中发现,这类题目在面试中常会要求:
- 现场推导时间复杂度
- 处理输入规模变化时的解法调整
- 解释去重逻辑的必要性
最后分享一个调试技巧:对于双指针问题,可以在循环中打印指针位置和当前值,直观观察搜索过程。
