资讯动态

Day 2刷题实战:哈希表与双指针,复杂度分析入门指南

发布时间:2026/10/20 21:17:25 来源:尧图企业网站定制
从第一天追到第二天编程题练习的习惯算是初步立住了。昨天还在纠结输入输出格式今天已经开始享受“把思路变成代码”的快感了。Day 2我用的是“热身题一道稍难的数组题一道字符串题”的组合整体节奏保持在一小时左右。今天练完的最大感受是做题顺序真的很关键先易后难能快速进入状态而坚持把每一步的复杂度分析写下来比“AC了就完事”有用太多。今天这组题型主要围绕数组、哈希表和双指针展开都是面试里非常高频的基础类型。如果你也正在坚持刷题欢迎对照着我今天的节奏来一遍也许能帮你把“只会看题解”变成“自己推出来”。1. Day 2练习的内容定位与目标拆解1.1 为什么第二天要开始侧重“思路复盘”而不是盲目刷量很多人刷题第一天热情拉满一口气吞下八九道题第二天发现前一天的解法全忘光了。这不是记忆力差而是大脑在收到过量信息时往往会优先丢弃“没有形成框架”的内容。所以Day 2我刻意把题目数量压到了三到四道但把每道题的思考时间拉长特别是强迫自己先写伪代码再写真实代码。比如我今天处理“查找数组中第一个不重复的字符”这种基础题时没有直接上手写哈希表而是先在草稿纸上把“字符映射次数”的过程画了一遍标注出最坏情况下需要扫描两趟数组时间复杂度是O(n)。这个看起来很笨的习惯在第2道双指针题里帮我省了至少二十分钟——因为我已经习惯了先估算复杂度再动手避免了写出一个能跑但性能糟糕的版本。Day 2的另一个目标是建立“解题流程感”。第一天的重点往往是熟悉语法和常用容器第二天则应该把注意力转移到“读题-分析约束-选数据结构-设计算法-写代码-走查用例”这条链路上。如果这一步养成习惯了后面练动态规划、递归回溯时会轻松很多。1.2 这类练习适合谁如果你的情况符合下面任何一条Day 2的内容建议认真跟练一遍刚开始刷题觉得“看得懂题解但自己写不出来”的人。需要在一个月内完成笔试或者在线测评时间紧但不想盲目刷海量题目的人。已经工作但想找回算法手感平时只能抽出碎片时间的人。今天这组题难度适中不需要高深的数学基础也不需要提前掌握复杂的算法模板。只要熟悉一门语言的循环、数组和字典/哈希表基本用法就能完整跟下来。我在文章后半部分还会给出完整代码和踩坑记录你可以直接对照运行。2. Day 2核心知识体系哈希表、双指针、复杂度估算2.1 为什么哈希表是基础题里的“大杀器”今天的练习里有一道题是“判断数组中是否存在两个数之和等于目标值”这是最经典的入门题之一。最自然的想法是双层循环遍历所有两两组合代码确实简短但时间复杂度是O(n²)。当数组长度跑到一万以上时运行时间会从毫秒级变成秒级在真实笔试环境里很容易直接超时。优化的核心思路就是用空间换时间。具体到这道题我们可以借助哈希表在Python里是dictJavaScript里是Map或普通对象把“要找的数”记下来。每遍历到一个数就去哈希表里查找“目标值减去当前数”是否已经出现过。这样一来查找操作的平均时间复杂度降到O(1)整个算法的复杂度就是O(n)只需要一趟遍历。这里有一个容易困惑的细节哈希表到底应该先存数再查还是边查边存正确的做法是边查边存因为我们要找的是“已有的配对”如果先一次性把所有数都存进去再重新遍历查找就有可能把“同一个数本身”误当成一对比如目标值6数组里有一个3先存后查时会错误地认为336成立但数组中只有一个3并不能组成有效配对。边查边存则天然规避了这个陷阱。2.2 双指针的直觉与适用范围今天第二道题是“原地反转字符串数组”我选择了双指针解法。所谓双指针就是维护一左一右两个下标每次交换左右指向的字符然后左指针右移、右指针左移直到两者相遇。由于只遍历了大约一半的元素时间复杂度是O(n)额外空间复杂度是O(1)完美满足“原地操作”的要求。这个解法初看很简单但很多人在写交换逻辑时容易写出如下边界问题当数组长度是偶数时左右指针会“错肩而过”但内部的while条件若是left right理论上不会越界当长度是奇数时中间那个字符不需要交换。我第一次写的时候就因为循环条件多写了一个等号导致中位字符被交换了两次结果字符串看起来没变但仔细推敲逻辑其实是错的。这也是双指针题里最常见的测试点一定要用“长度为奇数和偶数”两组用例分别验证。2.3 复杂度估算习惯的养成刷题时很多人会忽略复杂度估算觉得“能跑通就行”。Day 2我专门给自己列了一个小公式速查一层循环遍历数组 - O(n)两层嵌套循环 - O(n²)每次查找用哈希表 - 单次O(1)整体可以接受数组一半元素操作 - O(n/2)复杂度含义等同O(n)有了这个清单我在动手前就会快速判断方案可行性。比如一道题如果数据范围是10^5那么O(n²)基本没戏只有往O(n log n)甚至O(n)的方向思考才有活路。这种敏感度只能靠刻意练习获得千万不要跳步。3. Day 2经典题型拆解与完整代码实现3.1 题目一求两数之和返回下标题目描述很常见给定一个整数数组和一个目标值找出数组中和为目标值的两个数的下标假设每种输入只对应一个答案且不能使用同一个元素两次。我给出的Python版本实现如下def two_sum(nums, target): seen {} for i, num in enumerate(nums): need target - num if need in seen: return [seen[need], i] seen[num] i return []这段代码的核心逻辑很简单遍历到第i个数时先把需要的另一个数need算出来去seen里查。如果存在直接返回之前记录的下标和当前下标如果不存在就把当前数和它的下标存进seen。很多初学者会在这里反问为什么不先把所有元素都存进哈希表再二次遍历前面已经提过“重复元素被误匹配”的问题这里再补充一个例子nums [3, 3]target 6。先存完再遍历第一次遇到3时查找6-33在哈希表里是存在的但那个元素其实就是当前元素本身下标相同不满足“不能使用同一个元素两次”。除非额外增加“找到的下标不能等于当前下标”的判断否则就会出错。边查边存则不需要这种额外判断逻辑更干净。写完这段代码后建议立即验证四个用例print(two_sum([2, 7, 11, 15], 9)) # 期望 [0, 1] print(two_sum([3, 2, 4], 6)) # 期望 [1, 2] print(two_sum([3, 3], 6)) # 期望 [0, 1] print(two_sum([1, 2, 3], 7)) # 期望 []第二和第三组用例重点检查“元素重复”和“无解”两种边界场景很容易暴露实现缺陷。3.2 题目二反转字符串原地修改这道题通常要求输入是字符数组不能额外分配空间必须在原数组上修改。JavaScript的实现可以这样写function reverseString(s) { let left 0; let right s.length - 1; while (left right) { [s[left], s[right]] [s[right], s[left]]; left; right--; } return s; }这里用到了解构赋值来实现交换代码很简洁但要注意解构赋值会创建临时数组不过这个数组只存在于表达式内部不影响“原地修改”的整体复杂度判断。如果面试严格考察“不使用额外空间”可以改成传统的临时变量交换let temp s[left]; s[left] s[right]; s[right] temp;在笔试环境中解构赋值的写法没有问题但面试手撕代码时建议用临时变量版本并主动解释清楚为什么它满足O(1)空间复杂度。测试用例方面一定记得同时验证偶数和奇数长度console.log(reverseString([h, e, l, l, o])); // [o, l, l, e, h] console.log(reverseString([H, a, n, n, a, h])); // [h, a, n, n, a, H]遇到第二个用例时指针会分别指向两端一步步向中间走。用“奇数长度多出来的中间元素不动”这个规则去推演一遍比直接跑测试更能加深印象。3.3 题目三找出第一个不重复字符的下标这道题是哈希表的进阶用法。先遍历一趟字符串统计每个字符出现的次数再遍历第二趟找到第一个出现次数为1的字符返回下标不存在则返回-1。Python实现def first_unique_char(s): from collections import Counter count Counter(s) for i, ch in enumerate(s): if count[ch] 1: return i return -1使用Counter可以一行完成统计但如果在笔试环境里不允许使用标准库可以用普通字典手动统计count {} for ch in s: count[ch] count.get(ch, 0) 1两趟遍历的总时间复杂度是O(n)额外空间是O(k)其中k是不同字符的数量。这个解法很容易理解但容易忽略的一个点是为什么要两趟而不是一趟原因是第一趟必须先把所有字符出现次数完整统计出来第二趟才能判断某个字符是不是“只出现一次”。如果只遍历一趟在遇到中间的字符时根本不知道后面的字符会不会再次出现同一个字母。这个逻辑想明白了对这种“统计查询”类题目的理解就会更扎实。4. 实操中绕不开的坑边界条件与细节错误实录4.1 两数之和的“误用自身”问题我在第一次手写两数之和时就踩了“先存后查”的坑。当时的代码是seen {} for i, num in enumerate(nums): seen[num] i for i, num in enumerate(nums): need target - num if need in seen and seen[need] ! i: return [i, seen[need]]这个版本必须加seen[need] ! i这个判断来防止匹配到自身虽然能通过测试但逻辑比边查边存复杂。更重要的是这种写法在遇到重复元素时需要额外处理“应该返回哪一个下标”的问题容易引发歧义。对比之下边查边存版本天然规避了这类问题推荐优先掌握。4.2 反转字符串的循环边界反转字符串最容易出错的是循环条件。如果写成while (left right)在偶数长度数组的时候最后一轮交换的是中间两个元素完成后left和right恰恰相邻下一次进入循环时left right实际上最后一次交换是必要的但如果长度是奇数left等于right时交换同一个元素相当于没有操作属于多余操作不影响正确性只是逻辑不干净。写成while (left right)则统一适用于奇偶两种情况语义上更准确。另一个细节是数组为空或只有一个字符的极端情况。此时while条件直接不成立代码返回原数组。这个“空转”行为是安全的不需要额外判断。4.3 第一题中的易错点哈希表更新时机的顺序边查边存虽然能避免“自身匹配”但要注意更新的顺序。如果你把seen[num] i放在查找之前会破坏后续逻辑吗事实上对于两数之和来说先存后查和先查后存的差异主要体现在“第一个元素会不会立即被自己匹配”。比如target6第一个元素是3时如果先把3存进seen再查找need3就会发现seen里已经有3直接返回了相同下标。这就是为什么必须“先查再存”。很多文章只给了最终代码没有解释这个先后顺序的原因今天专门提一下希望帮你少踩一次坑。5. 常见问题排查速查表与调试技巧5.1 典型报错与排查思路下面的表格汇总了我练习Day 2时遇到的高频问题、可能原因和解决方向可以直接对照着检查。问题现象可能原因排查方向运行结果与期望值差一个下标边查边存的顺序写反了检查是否“先查再存”避免匹配自身字符串反转后原数组没变化解构赋值写在了函数内部但返回值未接收原地修改后直接返回数组本身即可空数组或单元素数组报错没有处理边界条件while循环越界确认循环条件是left right而非left right结果超时数据量大时卡住使用了O(n²)暴力解法改造成哈希表或双指针降低复杂度统计字符次数不准确字典的get默认值使用错误确认count.get(ch, 0)第2个参数是默认值05.2 定位问题的三个实用手段代码不通过时不要急着看题解。我这几年的习惯是按顺序做三类检查。第一步检查边界输入。把数组长度为0、长度为1、所有元素都相同、目标值与现有元素完全相同这几种极端情况跑一遍往往能快速暴露逻辑漏洞。第二步在关键位置打印中间状态。比如两数之和里在if内打印need和seen内容可以清晰看到哈希表增长顺序反转字符串里打印每一轮的left和right能直观感受指针移动是否符合预期。打印几行代码消耗的时间很少但比默读代码强十倍。第三步重置思路回到“人肉模拟”。找一张纸把数组写在上面手动模拟一遍算法过程尤其是在出错的那一步前后画清楚当前数据和状态。这个动作看起来原始但能在十分钟内解决靠读代码半小时也解决不了的逻辑问题。5.3 从“做对题”到“会讲题”Day 2我还额外做了一个练习每做完一道题用三句话解释自己的解法。第一句话说明用了什么数据结构第二句话说明核心循环是怎么工作的第三句话说明时间和空间复杂度。这个习惯的灵感来自模拟面试经验很多人代码能写对但被问到“为什么这样做”就卡壳。如果解释不清楚说明理解还不够透彻下次换个皮又不会了。6. 今日练习总结与扩展延伸建议6.1 今天练完后一定要掌握的三件事第一件手写两数之和的“边查边存”写法能解释为什么先存后查会产生误匹配。第二件双指针反转字符串能说清奇偶长度时循环条件的差异。第三件用“遍历三件套”即边界、单步、全量来自查代码形成习惯后正确率明显提升。掌握了这三件事Day 2就算真正过关了。今天这三道题看起来基础但它们背后的哈希表思想、双指针思想、复杂度分析方法几乎出现在后续每一个进阶题目里。把地基打牢后面学到哈希映射的变体、滑动窗口、快慢指针时才不会发懵。6.2 后续练习可以怎么延伸如果今天的题做完还有余力建议尝试以下几个变体难度逐步递增非常有助于巩固今日所学两数之和的变体输入数组已排序尝试用双指针从两端向中间逼近把空间复杂度从O(n)降到O(1)。反转字符串的变体不反转整个字符串而是反转字符串中的每个单词单词内部顺序不变但单词间顺序反转。找不重复字符的变体不再找第一个而是找出所有只出现一次的字符并按原顺序输出。这些变体不会完全超出今天的能力圈但每一个都强迫你重新思考“哈希表存什么”“指针怎么走”这两个核心问题。我个人经验是把一个基础题吃透五个变体比闷头刷二十道新题效果更好因为面试时真正考察的不是“做过没有”而是“能不能灵活变形”。6.3 关于坚持练习的一个小建议做编程题最怕的不是不会做而是今天做了明天忘。我自己用的复习节奏是“三天一轮”Day 2做完的题Day 4不看书重写一遍Day 6再改写成另一种语言或者换一种解法。第三次重写时很多解题步骤已经内化成直觉面对面试官提问也能更快组织语言。今天这篇文章里所有代码我都实际运行过确认能直接使用。如果你的运行结果和文章不一致大概率是环境版本差异导致的输出格式问题可以尝试打印中间变量检查具体差异。编程刷题这条路前期最快乐的部分就是每天都能看到自己的进步坚持到Day 10再回头看Day 2的代码你会明显感觉到思维层面的变化。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑