1. LeetCode刷题入门:从第28届Day01开始
刷算法题这件事,就像健身一样需要持续坚持。最近LeetCode官方推出了第28届每日一题挑战,我决定从Day01开始完整跟进这个系列。作为有五年刷题经验的"老油条",我想分享如何高效利用这类每日挑战活动,特别是针对刚开始刷题的新手朋友们。
这个Day01的题目虽然看似简单,但包含了几个经典算法思维模式。我建议无论基础如何,都值得认真对待每一道题,因为很多面试中的难题往往就是由这些基础元素组合而成的。接下来我会拆解题目解法,同时分享我的刷题工作流,包括如何做笔记、整理错题本、以及利用哪些工具提升效率。
2. 题目分析与解题思路
2.1 题目内容还原
第28届Day01的题目是经典的"两数之和"变种。给定一个整数数组nums和一个目标值target,要求在数组中找到两个数,使它们的和与target的绝对差最小,并返回这两个数的索引。这与传统的两数之和不同,不是找确切相等而是找最接近的值。
示例输入:
nums = [3, 7, 12, 18], target = 10
预期输出:
[0, 1] // 因为3+7=10,正好匹配
2.2 解题思路拆解
面对这类问题,我通常会采用"暴力法→优化思路→最终方案"的三步思考法:
-
暴力枚举:双重循环检查所有可能的数对组合,计算它们和与target的差值,记录最小差值对应的索引。时间复杂度O(n²),适用于小数据量。
-
排序+双指针:先对数组排序,然后用首尾指针向中间移动。计算当前两数和与target的差值,根据差值大小决定移动哪个指针。时间复杂度O(nlogn)来自排序。
-
哈希表优化:类似经典两数之和解法,但需要额外处理差值比较。建立值到索引的映射,对于每个元素nums[i],检查target-nums[i]是否存在于哈希表中。
经过实际测试,对于LeetCode的测试用例规模,第二种方法在编码复杂度和性能之间取得了最好平衡。下面重点讲解这种实现。
3. 代码实现与调试过程
3.1 排序与双指针解法
python复制def twoSumClosest(nums, target):
nums_sorted = sorted([(num, i) for i, num in enumerate(nums)], key=lambda x: x[0])
left, right = 0, len(nums_sorted) - 1
min_diff = float('inf')
result = []
while left < right:
current_sum = nums_sorted[left][0] + nums_sorted[right][0]
current_diff = abs(current_sum - target)
if current_diff < min_diff:
min_diff = current_diff
result = [nums_sorted[left][1], nums_sorted[right][1]]
if current_sum < target:
left += 1
else:
right -= 1
return sorted(result)
关键点说明:
- 使用元组保存原始索引,避免排序后丢失位置信息
- 初始化min_diff为无穷大,确保第一次比较必然更新
- 返回前对索引排序,保证输出顺序统一
3.2 边界条件测试
在提交前我总会手动测试这些边界情况:
- 空数组输入:应返回空列表
- 仅有一个元素:根据题目要求可能返回空或报错
- 所有元素相同:如[5,5,5], target=10
- 存在多个解:如[1,2,3,4], target=5
- 包含负数:如[-3, -1, 2, 4], target=1
提示:LeetCode的测试用例经常会包含这些边界情况,提前测试可以避免多次提交失败。
4. 复杂度分析与优化空间
4.1 时间复杂度分解
- 排序操作:O(nlogn),使用Python内置的Timsort算法
- 双指针遍历:O(n),每个元素最多被访问一次
- 总体复杂度:O(nlogn)主导
相比暴力法的O(n²),当n=10^5时,nlogn≈1.6×10^6,而n²=10^10,效率提升显著。
4.2 空间复杂度考量
- 存储带索引的排序数组:O(n)额外空间
- 其他变量:O(1)常数空间
- 总体:O(n)
如果题目允许修改原数组,可以原地排序并将索引单独存储,但会增加代码复杂度。在面试中需要与面试官讨论这种取舍。
5. 刷题工作流分享
5.1 我的每日刷题流程
-
题目分析(15分钟):
- 仔细阅读题目3遍
- 手写2-3个测试用例
- 画图辅助理解(特别是树/图类题目)
-
解法构思(20分钟):
- 先想暴力解法
- 思考优化方向
- 对比不同数据结构适用性
-
代码实现(25分钟):
- 先写伪代码
- 逐步实现各函数
- 添加详细注释
-
测试调试(15分钟):
- 运行示例测试用例
- 添加边界条件测试
- 使用print调试关键变量
-
总结归纳(10分钟):
- 记录到Notion知识库
- 标注题目类型和技巧标签
- 写解题心得
5.2 工具链配置
-
VS Code配置:
- LeetCode插件:直接提交和测试
- Code Runner:快速执行单文件
- Rainbow Brackets:匹配括号高亮
-
浏览器扩展:
- LeetCode Enhancer:显示题目通过率
- Video Speed Controller:调整解题视频速度
-
物理设备:
- 便携白板:模拟面试场景
- 机械键盘:提升编码手感
6. 常见问题与解决策略
6.1 调试技巧实录
问题:双指针移动条件判断错误
症状:对于输入[1,2,3,4,5], target=8,返回[2,3]而非[3,4]
排查:
- 打印每次循环的left/right指针位置
- 发现当left=2(right=4)时,3+5=8应该直接返回
- 检查移动逻辑发现current_sum == target时未做特殊处理
修复:
python复制if current_sum == target:
return sorted([nums_sorted[left][1], nums_sorted[right][1]])
6.2 效率提升技巧
-
变量预计算:
将频繁访问的nums_sorted[left][0]存入局部变量python复制
left_num, left_idx = nums_sorted[left] right_num, right_idx = nums_sorted[right] -
提前终止:
当找到差值为0的组合时立即返回python复制if min_diff == 0: break -
使用内置函数:
用min()替代if比较更新min_diffpython复制min_diff, result = min( (min_diff, result), (current_diff, [left_idx, right_idx]), key=lambda x: x[0] )
7. 题目变种与扩展思考
7.1 相似题目推荐
- 三数之和最接近(LeetCode 16)
- 较小的三数之和(LeetCode 259)
- 两数之和不同结构(使用BST或流数据)
7.2 实际应用场景
- 电商价格匹配:找最接近预算的商品组合
- 游戏伤害计算:技能组合达到特定伤害阈值
- 金融投资:选择两种资产使预期收益接近目标
7.3 进阶挑战
尝试解决以下变种问题:
- 找出所有满足差值小于k的组合
- 处理数据流中的实时查询
- 扩展到三维空间中的向量求和
这个Day01的题目虽然标为简单,但深入挖掘可以发现很多值得思考的维度。我建议每次刷题后花10分钟思考这些扩展方向,长期积累会显著提升算法思维。