LeetCode热题150详解

21 阅读1小时+

一、数组 / 字符串专题

数组与字符串为算法基础线性核心专题,涵盖双指针、滑动窗口、二分查找、前缀和、哈希映射五大核心解题模型;依托遍历压缩、边界收缩、预处理统计、快速映射等核心思想,摒弃暴力嵌套循环,针对性优化时间复杂度,可高效解决子串子数组、区间查询、元素匹配、去重计数、有序检索等高频经典题型,是线性序列类题目通用且覆盖面极广的综合解题体系。

88. 合并两个有序数组(简单)

题目简述:给你两个按 非递减顺序 排列的整数数组 nums1nums2,另有两个整数 mn ,分别表示 nums1nums2 中的元素数目。请你 合并nums2nums1 中,使合并后的数组同样按 非递减顺序 排列。注意:最终合并后数组不应由函数返回,而是存储在数组nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素是需要合并的有效元素,后 n 个元素为 0,属于占位空元素,可直接覆盖。

解题思路一:逆序双指针(最优原地解法)

核心原理:正序双指针遍历会覆盖 nums1 尚未遍历的有效元素,因此采用从后往前逆序遍历的方式。利用两个数组有序的特性,每次选取两个数组尾部的较大值,从 nums1 尾部开始填充,彻底规避元素覆盖问题。遍历结束后,若 nums2 仍有未合并的剩余元素,直接批量填充至 nums1 头部即可,全程原地操作无额外空间开销。

解题步骤:1. 定义指针 i 指向 nums1 有效元素末尾,指针 j 指向 nums2 末尾,指针 k 指向 nums1 整体末尾;2. 循环遍历,对比两个指针指向的元素,将较大值赋值给 nums1[k],对应指针前移;3. 循环结束后,兜底填充 nums2 剩余元素至 nums1 头部。

复杂度分析:时间复杂度 O(m+n),空间复杂度 O(1)

解题思路二:合并后排序(暴力简易解法)

核心原理:忽略数组有序特性,先将 nums2 所有元素直接合并填充到 nums1 的尾部空位,再对整个 nums1 数组进行整体排序。逻辑极简、上手快速,适合刷题快速通过,缺点是时间复杂度较高,不适用于大数据量场景。

解题步骤:1. 将 nums2 数组元素全部拷贝至nums1 后 n 位;2. 对完整的 nums1 数组进行升序排序。

复杂度分析:时间复杂度 O((m+n)log(m+n)),空间复杂度 O(log(m+n))(排序递归栈空间)

易错点总结:正序遍历导致 nums1 有效元素被覆盖;遗漏 nums2 剩余元素的兜底填充;暴力排序效率低下,无法适配大规模数据。

示例测试:输入 nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3,输出 nums1 = [1,2,2,3,5,6]

27. 移除元素(简单)

题目简述:给你一个整数数组 nums 和一个整数 val,请你 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间原地 修改输入数组。元素的顺序可以改变,你不需要考虑数组中超出新长度后面的元素。

解题思路一:快慢双指针(标准最优解)

核心原理:数组原地去重/移除元素经典模板。慢指针用于记录有效元素的存储位置,快指针全程遍历整个数组筛选有效元素。快指针遍历到不等于 val 的有效元素时,将元素赋值给慢指针位置,同时移动慢指针;遍历到等于 val 的无效元素则直接跳过。最终慢指针的数值即为移除元素后的有效数组长度。

解题步骤:1. 初始化快慢指针均为 0;2. 快指针逐个遍历数组所有元素,筛选有效元素并赋值给慢指针位置;3. 遍历完成后,慢指针数值即为有效数组长度,直接返回。

复杂度分析:时间复杂度O(n),空间复杂度 O(1)

解题思路二:左右对撞指针(优化交换解法)

核心原理:适配待删除元素数量较多的场景,优化快慢指针的频繁赋值操作。左指针从数组头部向后遍历,查找需要删除的目标元素;右指针从数组尾部向前遍历,查找需要保留的有效元素。找到对应元素后交换两者位置,不断缩小遍历区间,最终完成无效元素移除。

解题步骤:1. 初始化左指针、右指针分别指向数组首尾;2. 左指针找到无效元素、右指针找到有效元素后交换;3. 循环直至两指针相遇,左指针数值即为有效长度。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

易错点总结:快慢指针移动时机混淆,导致有效元素丢失;对撞指针边界判断错误,出现越界或漏删元素;未遵循原地修改规则,使用额外数组存储元素。

示例测试:输入 nums = [3,2,2,3], val = 3,输出有效长度 2,数组前两位有效元素为 [2,2]

26. 删除有序数组中的重复项(简单)

题目简述:给你一个 升序排列 的整数数组 nums,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的相对顺序 应该保持 一致。不要使用额外的数组空间,你必须在 原地 修改输入数组 并使用 O(1) 额外空间 完成。无需考虑数组中超出新长度后面的元素。

解题思路一:快慢双指针(有序数组去重模板)

核心原理:利用数组升序特性,所有重复元素必然相邻。慢指针用于存储当前最后一位唯一元素,快指针向前遍历数组,寻找全新的不重复元素。当快慢指针指向元素不一致时,说明快指针找到新的唯一元素,更新慢指针位置并赋值,完成单次去重,遍历结束即可得到无重复有序数组。

解题步骤:1. 慢指针初始化为 0,快指针从 1 开始遍历数组;2. 快指针元素与慢指针元素不同时,慢指针后移,更新慢指针位置元素;3. 遍历结束,慢指针 + 1 为有效数组长度。

复杂度分析:时间复杂度 O(n),空间复杂度O(1)

解题思路二:遍历计数覆盖(新手简易思路)

核心原理:遍历数组,实时统计当前元素的连续重复次数,遇到与前一位不同的新元素时,直接将元素覆盖到有效数组的末尾位置。逻辑直白易懂,本质与快慢指针思路一致,更适合新手理解去重逻辑。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

易错点总结:指针初始位置设置错误;混淆有序数组与无序数组去重逻辑;最终有效长度忘记 +1,导致结果偏差。

示例测试:输入 nums = [1,1,2],输出有效长度 2,数组前两位有效元素为 [1,2]

80. 删除有序数组中的重复项 II(中等)

题目简述:给你一个升序排列 的整数数组 nums ,请你 原地 删除重复出现的元素,使得每个元素 最多出现两次 ,返回删除后数组的新长度。不要使用额外的数组空间,必须在 原地 修改输入数组 并使用 O(1) 额外空间 完成。元素相对顺序保持不变,无需考虑超出新长度的数组元素。本题是 26 题的进阶版本,支持可控重复次数去重。

解题思路一:通用快慢指针(最优模板解法)

核心原理:可通用适配「数组元素最多出现 k 次」的去重场景(本题 k=2)。基于数组有序特性,若当前遍历元素与慢指针前第二位元素相同,说明当前元素已出现超过 2 次,属于超额重复元素,直接跳过;若不相同,说明元素合法,保留并更新慢指针位置。前两位元素必然合法,无需校验。

解题步骤:1. 数组长度 ≤ 2 时直接返回数组长度;2. 慢指针初始化为 2,快指针从 2 开始遍历数组;3. 对比 nums[fast]nums[slow-2],不相等则更新慢指针元素;4. 遍历结束,慢指针数值即为有效数组长度。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

解题思路二:计数去重法(直观易懂解法)

核心原理:遍历数组,实时记录当前元素的连续出现次数,精准控制重复次数。当元素连续出现次数 ≤2 时,保留该元素;次数超过 2 时,判定为无效元素直接跳过,最终完成合规去重。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

易错点总结:错误对比慢指针当前位置元素,导致保留超额重复元素;元素切换时重复计数未及时重置;边界元素校验遗漏。

示例测试:输入 nums = [1,1,1,2,2,3],输出有效长度 5,数组前五位有效元素为 [1,1,2,2,3]

169. 多数元素(简单)

题目简述:给定一个大小为n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。题目保证数组中一定存在多数元素,且多数元素唯一。你可以假设数组非空,且总能找到符合条件的元素。

解题思路一:摩尔投票法(最优解法)

核心原理:多数元素的数量大于其余所有元素的数量总和,利用「不同元素相互抵消」的核心逻辑实现零空间求解。遍历数组过程中不断抵消不同元素,最终无法被抵消、剩余的候选元素,必然是数组的多数元素。

解题步骤:1. 初始化计数 count = 0,候选元素为空;2. count 为 0 时,更新当前元素为候选元素;3. 当前元素等于候选元素则 count+1,否则 count-1;4. 遍历结束,候选元素即为多数元素。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

解题思路二:哈希表统计法(新手直观解法)

核心原理:遍历数组,通过哈希表统计每个元素的出现频次,建立「元素-出现次数」的映射关系。遍历哈希表,筛选出出现次数大于 n/2 的元素,即为答案。逻辑简单直观,极易理解。

复杂度分析:时间复杂度 O(n),空间复杂度 O(n)

解题思路三:数组排序法(极简暴力解法)

核心原理:由于多数元素出现次数超过数组长度的一半,数组升序排序后,数组中间位置的元素必然是多数元素,无需遍历统计频次,直接取值即可。

复杂度分析:时间复杂度 O(nlogn),空间复杂度 O(logn)

易错点总结:摩尔投票法仅适用于「一定存在多数元素」的场景,无该条件需二次校验;哈希表遍历遗漏元素;排序后取值下标错误。

示例测试:输入 nums = [2,2,1,1,1,2,2],输出结果:2

189. 轮转数组(中等)

题目简述:给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。要求 原地修改数组,不可返回新数组,尽可能减少额外空间开销。注意:k 可能大于数组长度,超出长度的轮转为无效重复操作,需要预处理消除。

解题思路一:三次反转法(最优原地解法)

核心原理:利用数组反转特性实现零额外空间轮转,是数组轮转经典最优模板。数组向右轮转 k 位的等价操作:整体反转数组 → 反转前 k 个元素 → 反转后 n-k 个元素。需先对 k 取模(k = k % n),消除无效轮转,避免多余操作。

解题步骤:1. 预处理:k = k % 数组长度 n;2. 反转整个数组;3. 反转区间 [0, k-1] 的元素;4. 反转区间 [k, n-1] 的元素,完成轮转。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

解题思路二:临时数组拷贝法(刷题简易解法)

核心原理:借助临时数组存储轮转后的完整结果,规避复杂的原地反转操作。原数组下标 i 的元素,轮转后对应新数组下标 (i + k) % n,批量赋值后拷贝回原数组即可。

复杂度分析:时间复杂度 O(n),空间复杂度 O(n)

易错点总结:未对 k 取模,导致无效反转、超时出错;反转区间边界判断错误;临时数组下标映射关系混乱,元素错位。

示例测试:输入 nums = [1,2,3,4,5,6,7], k = 3,输出轮转后数组:[5,6,7,1,2,3,4]

121. 买卖股票的最佳时机(简单)

题目简述:给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天买入 这只股票,并选择在未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。如果你不能获取任何利润,返回 0。核心限制:只能单次交易,先买后卖,不可反向操作。

解题思路一:一次遍历贪心(最优解法)

核心原理:单次股票交易最优贪心策略。遍历过程中持续记录已遍历区间的最小股票价格,实时计算「当前价格 - 最小价格」的利润,不断更新全局最大利润,每一步都保留当前最优解,一次遍历即可完成计算。

解题步骤:1. 初始化最小价格为无穷大,最大利润为 0;2. 遍历每日股价,更新历史最小价格;3. 实时计算当日利润,更新全局最大利润;4. 遍历结束返回最大利润。

复杂度分析:时间复杂度O(n),空间复杂度 O(1)

解题思路二:动态规划法(股票通用框架)

核心原理:股票系列通用 DP 解题框架,适配各类股票交易题型。定义两个状态:dp0 表示当前不持有股票的最大利润,dp1 表示当前持有股票的最大利润,通过状态转移迭代更新最优解,可空间优化至常量级别。

复杂度分析:时间复杂度 O(n),空间复杂度O(1)

易错点总结:允许先卖后买,违反交易规则;极值初始化错误,导致利润计算偏差;动态规划状态定义混淆。

示例测试:输入 prices = [7,1,5,3,6,4],输出最大利润:5

122. 买卖股票的最佳时机 II(中等)

题目简述:给你一个整数数组 prices ,其中 prices[i] 表示股票第 i 天的价格。你可以在每一天决定购买或卖出股票,可以无限次交易(多次买卖股票),但必须在再次购买前出售掉之前的股票(不允许持仓多股)。设计算法计算能获取的最大利润,无盈利则返回 0。

解题思路一:贪心算法(最优解法)

核心原理:无交易次数限制时,全局最优解等价于赚取所有正向差价。只要后一天股票价格高于前一天,就存在盈利空间,进行一次买卖交易;下跌则不操作。所有小额正向盈利累加,即为全局最大利润,局部最优累加得到全局最优。

解题步骤:1. 初始化总利润为 0;2. 从第二天开始遍历股价数组;3. 若后一天价格 > 前一天价格,累加差价至总利润;4. 遍历结束返回总利润。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

解题思路二:动态规划法(通用进阶解法)

核心原理:沿用股票通用 DP 状态定义,修改状态转移规则适配多次交易场景。不持仓的利润来自「前一天不持仓」或「当天卖出股票」;持仓的利润来自「前一天持仓」或「当天买入股票」,迭代更新最优利润。

复杂度分析:时间复杂度 O(n),空间复杂度O(1)

易错点总结:执着于单次最大差价,忽略多次小额盈利累加更优;DP 状态转移逻辑混淆,违反先卖后买规则。

示例测试:输入 prices = [7,1,5,3,6,4],输出最大利润:7

55. 跳跃游戏(中等)

题目简述:给定一个非负整数数组 nums ,你最初位于数组的第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标。可以跳跃的步数不固定,只要不超过当前位置最大步数即可,无需跳满最大步数。

解题思路一:全局贪心遍历(最优解法)

核心原理:实时维护全局最远可达位置,遍历每个可抵达的下标,不断刷新最远跳跃距离。若当前遍历下标超出最远可达位置,说明该位置无法抵达,后续位置均不可达,直接返回 false;若最远可达位置覆盖数组末尾下标,提前返回 true,终止遍历。

解题步骤:1. 初始化最远可达位置为 0;2. 遍历数组所有下标;3. 校验当前下标是否可达,不可达直接返回 false;4. 更新最远可达位置,覆盖末尾则提前返回 true。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

解题思路二:动态规划法(直观解法)

核心原理:定义 dp[i] 表示数组下标 i 是否可达。初始状态首位可达,遍历所有可达位置,标记其跳跃范围内的所有下标为可达,最终判断末尾下标是否可达。

复杂度分析:时间复杂度 O(n²),空间复杂度 O(n)

易错点总结:未提前终止循环,产生大量冗余遍历;忽略当前位置不可达的边界情况;最远位置更新时机错误。

示例测试:输入 nums = [2,3,1,1,4],输出 true;输入 nums = [3,2,1,0,4],输出 false

45. 跳跃游戏 II(中等)

题目简述:给定一个长度为 n0 索引整数数组 nums。初始位置为 nums[0]。数组每个元素代表当前位置可以跳跃的最大长度。题目保证一定可以到达数组末尾,请你返回到达数组最后一个位置的最小跳跃次数

解题思路一:区间分段贪心(最优解法)

核心原理:按跳跃区间分段遍历,维护两个核心变量:当前跳跃边界、下一步最远可达边界。在当前跳跃区间内遍历所有位置,持续更新下一步最远位置;当遍历到当前区间边界时,代表必须完成一次跳跃,步数+1,同时更新区间边界,直至覆盖数组末尾。

解题步骤:1. 初始化跳跃步数、当前边界、最远可达位置均为 0;2. 遍历区间 [0, n-2](避免多算最后一步);3. 实时更新最远可达位置,抵达当前边界则步数+1,更新边界。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

解题思路二:动态规划法(暴力优化解法)

核心原理:定义 dp[i] 为到达下标 i 的最小跳跃步数,初始所有步数为无穷大,首位步数为 0。遍历每个位置,更新其跳跃范围内所有位置的最小步数,最终返回末尾位置步数。

复杂度分析:时间复杂度 O(n²),空间复杂度 O(n)

易错点总结:遍历至最后一个元素,导致步数多算一次;区间边界更新时机错误,步数统计偏差。

示例测试:输入 nums = [2,3,1,1,4],输出最小跳跃步数:2

274. H 指数(中等)

题目简述:给你一个整数数组 citations ,其中 citations[i] 表示研究者的第i 篇论文被引用的次数。计算并返回该研究者的 h 指数。h 指数的定义:h 代表“高引用次数”,一名科研人员的 h 指数是指他(她)的h 篇论文至少每篇被引用 h 次,剩余的 n-h 篇论文每篇被引用次数不超过 h 次。要求找出满足条件的最大 h 值

解题思路一:排序+二分查找(高效解法)

核心原理:将数组升序排序后具备有序性,可通过二分枚举所有合法 h 值。不断收缩二分边界,判断当前 mid 值是否满足「至少 mid 篇论文引用 ≥ mid」,最终筛选出最大合法 h 值,大幅优化遍历效率。

复杂度分析:排序 O(nlogn),二分 O(logn),总时间 O(nlogn),空间 O(logn)

解题思路二:计数排序(线性最优解法)

核心原理:h 指数的最大值不超过论文总数 n,无需统计超大引用次数。新建计数数组统计 0~n 区间内的论文数量,逆序遍历计数数组,累加论文数量,找到第一个满足数量≥下标值的下标,即为最大 h 值。

复杂度分析:时间复杂度 O(n),空间复杂度 O(n)

解题思路三:暴力遍历(新手入门解法)

核心原理:枚举 h 的所有可能取值(0~n),逐个校验每个 h 值是否满足 h 指数规则,记录遍历过程中最大的合法 h 值。逻辑简单无门槛,仅适配小规模数据。

复杂度分析:时间复杂度 O(n²),空间复杂度 O(1)

易错点总结:h 取值范围判断错误;排序后区间对应关系混淆;计数数组边界处理不当,结果出错。

示例测试:输入 citations = [3,0,6,1,5],输出 h 指数:3

380. O(1) 时间插入、删除和获取随机元素(中等)

题目简述:实现 RandomizedSet 类,要求实现三个操作,且所有操作均满足 平均 O(1) 时间复杂度:1. insert(val):当元素 val 不存在时,向集合中插入该项,并返回 true;否则返回 false;2. remove(val):当元素 val 存在时,从集合中移除该项,并返回 true;否则返回 false;3. getRandom():随机返回现有集合中的一项,每个元素需要拥有相同的概率。元素可重复插入,集合内元素唯一。

解题思路:数组+哈希表结合(唯一最优解法)

核心原理:单独数据结构无法满足所有 O(1) 操作:动态数组支持 O(1) 随机访问,但删除中间元素为 O(n);哈希表支持 O(1) 查找插入,但无法随机取值。两者结合互补:数组存储真实元素用于随机取值,哈希表存储「元素-数组下标」映射用于快速定位。删除时采用末尾交换法,将待删元素与数组末尾元素交换,再弹出末尾元素,规避数组移位开销。

解题步骤:1. 插入:哈希表判重,无重复则数组追加元素、记录下标;2. 删除:定位元素下标,交换末尾元素,更新哈希映射,删除末尾元素;3. 随机获取:随机生成下标,返回数组对应元素。

复杂度分析:插入、删除、随机获取均为 O(1) 平均时间复杂度,空间 O(n)

易错点总结:删除元素后未更新交换元素的哈希下标映射,导致数据错乱;插入元素未判重,违反集合唯一性规则。

示例测试:实例化集合后,依次执行 insert(1)、insert(2)、remove(1)、getRandom(),随机返回结果:2

238. 除自身以外数组的乘积(中等)

题目简述:给你一个整数数组 nums,返回一个数组 answer ,其中answer[i] 等于 nums 中除 nums[i] 之外其余所有元素的乘积。要求:1. 算法时间复杂度 O(n);2. 禁止使用除法运算;3. 尽可能降低额外空间复杂度。数组元素可正可负,包含 0。

解题思路一:前后缀乘积优化(O(1)额外空间最优解)

核心原理:任意位置的结果值 = 当前位置左侧所有元素乘积 × 当前位置右侧所有元素乘积。无需单独开辟前后缀数组,直接复用结果数组存储前缀积,再通过变量迭代维护后缀积,两次遍历完成计算,实现极致空间优化。

解题步骤:1. 正序遍历数组,在结果数组中存储每个位置的左侧前缀乘积;2. 逆序遍历数组,用变量记录右侧后缀乘积,叠加计算最终结果;3. 遍历结束返回结果数组。

复杂度分析:时间复杂度 O(n),额外空间复杂度 O(1)(结果数组不计入额外空间)

解题思路二:前后缀数组法(直观易懂解法)

核心原理:单独开辟前缀数组、后缀数组,分别存储每个下标对应的左侧所有元素乘积、右侧所有元素乘积。最后遍历数组,将对应位置前后缀乘积相乘,得到最终结果,逻辑直白无难点。

复杂度分析:时间复杂度 O(n),空间复杂度 O(n)

易错点总结:使用除法求解,无法适配数组含 0 的场景;前后缀乘积初始值错误;遍历顺序颠倒,乘积计算偏差。

示例测试:输入 nums = [1,2,3,4],输出结果数组:[24,12,8,6]

134. 加油站(中等)

题目简述:在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的汽车,从第i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。若有解,答案唯一

解题思路一:单次遍历贪心(最优解法)

核心原理:核心判定定理:所有加油站总油量 ≥ 总消耗油量,一定存在合法起点;反之无解。遍历过程中累计当前行驶路径的剩余油量,若剩余油量为负,说明当前起点至当前位置无法通行,且区间内所有中间位置均不可能是合法起点,直接重置起点为下一站,清空累计油量。

解题步骤:1. 统计全局总油量、总耗油量;2. 遍历数组累计单次路径剩余油量;3. 油量不足则重置起点,最后根据全局油量判定返回结果。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

解题思路二:暴力枚举(新手入门解法)

核心原理:枚举每一个加油站作为起始起点,模拟环形行驶全程,实时更新油箱剩余油量。若能完整走完一圈则返回当前起点,所有起点均无法通行则返回 -1。

复杂度分析:时间复杂度 O(n²),空间复杂度 O(1)

易错点总结:局部油量不足直接否定全局可行性;未做全局油量校验,导致结果错误;起点重置逻辑混乱。

示例测试:输入 gas = [1,2,3,4,5], cost = [3,4,5,1,2],输出合法起点下标:3

135. 分发糖果(困难)

题目简述n 个孩子站成一排,每个孩子对应一个评分数组 ratings。需要按照以下规则给孩子分发糖果:1. 每个孩子至少分配 1 颗糖果;2. 如果两个相邻孩子评分不同,评分更高的孩子必须获得更多糖果。请你计算并返回需要准备的最少糖果总数

解题思路一:双向遍历贪心(标准最优解)

核心原理:单次遍历无法同时满足左右相邻评分规则,需双向遍历兜底。先从左向右遍历,保证右侧高分孩子糖果数多于左侧;再从右向左遍历,保证左侧高分孩子糖果数多于右侧。两次遍历取最大值,既满足所有规则,又保证糖果总数最少。

解题步骤:1. 初始化全 1 糖果数组;2. 左→右遍历,更新递增区间糖果数量;3. 右→左遍历,更新递减区间糖果数量,取最大值;4. 累加数组总和得到最少糖果数。

复杂度分析:时间复杂度O(n),空间复杂度 O(n)

解题思路二:单次遍历常量空间(进阶最优解)

核心原理:无需存储糖果数组,通过记录连续递增、递减序列的长度,单次遍历统计总糖果数。识别涨跌区间长度,根据数学规律计算对应糖果增量,实现 O(1) 空间复杂度,是进阶最优解法。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

易错点总结:单次遍历遗漏反向相邻规则;未取两次遍历最大值,糖果数量不满足规则;边界孩子糖果计算错误。

示例测试:输入 ratings = [1,0,2],输出最少糖果总数:5

42. 接雨水(困难)

题目简述:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。雨水只能存储在左右有更高柱子的凹槽中,柱子本身无法储水,需计算所有凹槽的总储水量。

解题思路一:双指针法(O(1)空间最优解)

核心原理:单柱储水公式:当前柱储水量 = min(左侧最大高度, 右侧最大高度) - 当前柱高度。定义左右指针向中间收缩,同步维护左右两侧最大高度,每次移动高度更小的一侧指针(该侧为短板,决定储水量),逐列累加储水量,全程无额外数组开销。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

解题思路二:前后缀最大值法(直观解法)

核心原理:预处理两个数组,分别存储每个位置左侧最高柱子高度、右侧最高柱子高度。遍历每一根柱子,套用储水公式计算单柱储水量,累加得到总雨水量,逻辑清晰、极易调试。

复杂度分析:时间复杂度 O(n),空间复杂度 O(n)

解题思路三:单调栈法(进阶解法)

核心原理:维护单调递减栈存储柱子下标,遍历过程中遇到更高柱子时,形成凹槽结构。弹出栈底凹槽下标,计算当前凹槽的储水面积,按层累加雨水,适配复杂柱状图场景。

复杂度分析:时间复杂度 O(n),空间复杂度 O(n)

易错点总结:暴力逐行计算效率极低;错误使用当前高度计算储水;左右最大高度更新时机错误,储水量计算偏差。

示例测试:输入 height = [0,1,0,2,1,0,1,3,2,1,2,1],输出总储水量:6

13. 罗马数字转整数(简单)

题目简述:罗马数字包含以下七种字符:I(1)V(5)X(10)L(50)C(100)D(500)M(1000)。通常情况下,罗马数字中大数字在小数字左侧,表示数值相加;特殊情况:小数在大数左侧,表示减法(IV=4、IX=9、XL=40等)。给定一个合法的罗马数字字符串,将其转换为对应的整数,输入范围 1~3999。

解题思路一:一次遍历判断加减(最优解法)

核心原理:遍历字符串,对比当前字符数值与后一位字符数值:若当前数值 < 后一位数值,说明是特殊减法组合,减去当前值;否则累加当前值。一次遍历即可完成转换,无冗余操作。

解题步骤:1. 建立罗马字符与整数的映射表;2. 遍历字符串,逐位判断加减;3. 累加计算最终整数结果。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

解题思路二:特殊字符替换法(简易解法)

核心原理:提前将所有特殊减法组合(IV、IX、XL、XC、CD、CM)替换为自定义的独立加法字符,消除减法逻辑。遍历替换后的字符串,直接累加所有字符对应数值即可。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

易错点总结:遗漏特殊减法组合规则;最后一位字符未累加;字符数值映射关系对应错误。

示例测试:输入 s = "IV",输出 4;输入 s = "MCMXCIV",输出 1994

12. 整数转罗马数字(中等)

题目简述:罗马数字包含七种基础字符:I(1)V(5)X(10)L(50)C(100)D(500)M(1000)。同时包含六种特殊减法组合:IV(4)IX(9)XL(40)XC(90)CD(400)CM(900)。给定整数 num,输入范围为 1 ≤ num ≤ 3999,需要将其转换为合法且最短格式的罗马数字字符串。转换规则为优先使用大数值字符,特殊数值固定使用减法组合,保证书写规范。

解题思路一:贪心硬编码(最优标准解法)

核心原理:基于贪心思想,将所有基础数值和特殊组合数值按从大到小顺序预编码存储。遍历数值数组,每次优先匹配当前最大的可用数值,若当前数字大于等于该数值,则拼接对应罗马字符,并减去该数值,不断缩减原数,直至数值归零。该思路保证每次选取最优解,最终拼接出的字符串长度最短、格式合规。

解题步骤:1. 预定义从大到小排序的数值数组和对应罗马字符数组;2. 遍历所有数值,当当前 num ≥ 遍历数值时,拼接对应字符并更新 num;3. 当 num 归零时终止遍历,返回拼接后的字符串。

复杂度分析:时间复杂度 O(1),空间复杂度 O(1)(匹配次数固定,与输入数据无关)

解题思路二:按位拆解法(易懂解法)

核心原理:将整数拆解为千、百、十、个四位,为每一位单独预设0-9对应的罗马字符组合,逐位匹配、逐段拼接。拆分逻辑清晰,可直观对应每一位的转换规则,新手易理解、易调试。

复杂度分析:时间复杂度 O(1),空间复杂度 O(1)(整数位数固定为四位)

易错点总结:遗漏4、9、40、90、400、900特殊组合,导致结果格式不规范;字符拼接顺序颠倒;未限制输入区间,边界数值转换出错。

示例测试:输入 num = 3,输出 "III";输入 num = 58,输出 "LVIII";输入 num = 1994,输出 "MCMXCIV"

58. 最后一个单词的长度(简单)

题目简述:给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中最后一个单词的长度。单词是指仅由字母组成、不包含任何空格字符的最大子字符串。需要处理特殊边界:字符串末尾存在多余空格、字符串仅含单个单词、字符串首尾全为空格等场景。

解题思路一:倒序遍历统计(最优极简解法)

核心原理:无需遍历完整字符串,从字符串末尾开始倒序遍历,先跳过末尾所有多余空格,定位到最后一个单词的末尾字符,再继续向前遍历统计连续字母数量,即为最后一个单词长度。最大程度减少遍历次数,极致高效。

解题步骤:1. 初始化长度计数器为0,从字符串最后一位开始遍历;2. 倒序跳过所有末尾空格;3. 遇到字母则持续计数,直至遇到空格或遍历至字符串头部;4. 返回最终计数长度。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)

解题思路二:字符串分割(新手简易解法)

核心原理:利用语言内置分割方法,将字符串按空格分割并过滤空字符串,得到所有有效单词列表,直接取列表最后一个单词统计长度。逻辑极简,无需手动处理边界空格。

复杂度分析:时间复杂度 O(n),空间复杂度 O(n)(存储分割后的单词数组)

易错点总结:未处理末尾多余空格,统计到空字符长度;正序遍历冗余操作过多;分割后未过滤空字符串导致下标越界。

示例测试:输入 s = "Hello World",输出 5;输入 s = " fly me to the moon ",输出 4;输入 s = "luffy",输出 5

14. 最长公共前缀(简单)

题目简述:编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""。公共前缀是指所有字符串头部共有的连续字符序列,需匹配数组中每一个字符串,只要有一个字符串不匹配,当前字符即终止匹配。

解题思路一:横向扫描(基准匹配最优解)

核心原理:以数组第一个字符串为初始公共前缀基准,依次与后续每一个字符串逐字符比对,不断裁剪缩短公共前缀长度。每匹配完一个字符串,更新当前最长公共前缀,若前缀为空可提前终止遍历,无需后续匹配,大幅优化效率。

解题步骤:1. 特判数组为空时直接返回空字符串;2. 初始化公共前缀为第一个字符串;3. 遍历后续所有字符串,逐字符比对并裁剪前缀;4. 前缀为空则提前退出,最终返回公共前缀。

复杂度分析:时间复杂度 O(mn)(m为字符串平均长度,n为字符串个数),空间复杂度 O(1)

解题步骤:1. 特判字符串数组为空,直接返回空字符串;2. 以第一个字符串的字符长度为最大匹配位数,逐位遍历下标;3. 遍历其余所有字符串,校验当前下标字符是否匹配;4. 匹配失败立即截断,返回已匹配的前缀字符。

复杂度分析:时间复杂度 O(mn),空间复杂度 O(1)

易错点总结:未处理字符串长度不一致的边界,导致下标越界;匹配成功后未及时终止循环;空数组、单个字符串等特殊用例判断遗漏。

示例测试:输入 strs = ["flower","flow","flight"],输出 "fl";输入 strs = ["dog","racecar","car"],输出 ""

151. 反转字符串中的单词(中等)

题目简述:给你一个字符串 s ,反转字符串中单词的顺序,但单词内部字符顺序不变。单词定义为连续非空格字符,单词之间可能存在多个空格,字符串首尾也可能存在空格。要求处理后:1. 单词之间仅保留单个空格分隔;2. 首尾无多余空格;3. 尽可能实现低空间开销,最优解支持原地 O(1) 额外空间处理。

解题思路一:整体反转+单词反转+去空格(O(1)最优原地解法)

核心原理:不借助额外字符串存储结果,通过三次原地操作实现目标。第一步整体反转整个字符串,实现单词逆序;第二步逐个反转每个单词内部字符,还原单词原本顺序;第三步清理首尾及中间多余空格,保证格式规范。全程仅常数变量辅助,无额外空间开销。

解题步骤:1. 将字符串转为字符数组,方便原地修改;2. 整体反转整个字符数组;3. 遍历数组,分段反转每个完整单词;4. 双指针清理所有多余空格,规整字符串格式;5. 拼接为最终字符串返回。

复杂度分析:时间复杂度 O(n),空间复杂度 O(1)(不计字符数组转换开销)

解题思路二:分割遍历拼接(新手简易解法)

核心原理:利用字符串分割特性,过滤所有空字符串,提取全部有效单词,再逆序遍历单词列表,用单个空格拼接所有单词。逻辑简单直观,无需复杂的原地操作,适合快速刷题通过。

复杂度分析:时间复杂度O(n),空间复杂度 O(n)(存储单词列表)

易错点总结:未过滤连续空格和首尾空格,导致结果格式错误;单词反转区间边界判断偏差;逆序拼接时分隔符使用错误。

示例测试:输入 s = "the sky is blue",输出 "blue is sky the";输入 s = " hello world ",输出 "world hello"

6. Z 字形变换(中等)

题目简述:将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。排列完成后,按行优先顺序读取字符,拼接为新字符串返回。无需构建完整二维矩阵,可通过规律直接模拟字符分布。特殊边界:行数为 1 时,字符串无需变换,直接返回原串。

解题思路一:周期规律模拟(最优解法)

核心原理:Z 字形排列存在固定周期规律,一个完整周期的长度为 cycle = 2 * numRows - 2。每个周期内,第一行和最后一行仅出现一个字符,中间行出现两个字符。遍历每一行,根据周期公式直接计算对应下标字符,逐行拼接结果,无需模拟矩阵,极致节省空间。

解题步骤:1. 特判 numRows == 1,直接返回原字符串;2. 计算排列周期 cycle;3. 逐行遍历,先取周期内首个字符,中间行额外取对称位置字符;4. 遍历拼接所有字符,返回最终结果。

复杂度分析:时间复杂度 O(n),空间复杂度 O(n)(存储结果字符串)

解题思路二:行号模拟遍历(直观易懂解法)

核心原理:模拟字符的行进轨迹,维护当前行号和移动方向。向下遍历至最后一行后反向向上遍历,抵达首行后再次反向,不断往复。每个字符追加到对应行的字符串末尾,最终按行拼接所有字符串得到结果。

复杂度分析:时间复杂度 O(n),空间复杂度 O(n)

易错点总结:行数为1的边界未特判,导致计算出错;周期公式记忆错误;中间行双字符下标计算偏差;方向反转时机判断失误。

示例测试:输入 s = "PAYPALISHIRING", numRows = 3,输出 "PAHNAPLSIIGYIR";输入 numRows = 4,输出 "PINALSIGYAHRPI"

28. 找出字符串第一个匹配下标(简单)

题目简述:给你两个字符串 haystack(主串)和needle(模式串),请你在主串中找出第一个匹配模式串的起始下标。若存在匹配,返回对应起始下标;若不存在匹配,返回 -1。若模式串为空字符串,返回 0。本题为字符串匹配基础题型,简单场景暴力匹配即可高效通过,进阶可使用 KMP 算法优化。

解题思路一:滑动窗口暴力匹配(刷题最优简易解)

核心原理:在主串中以模式串长度为窗口大小,逐位滑动窗口。每次窗口内逐字符比对主串与模式串,若全部字符匹配成功,返回当前窗口起始下标;若匹配失败,窗口右移一位继续匹配,遍历结束无匹配则返回 -1。简单题数据量小,暴力匹配效率完全足够。

解题步骤:1. 特判模式串为空,直接返回 0;2. 计算主串和模式串长度,限定窗口滑动范围;3. 逐窗口字符比对,完全匹配则返回起始下标;4. 遍历结束未匹配成功,返回 -1。

复杂度分析:最坏时间复杂度 O(nm),空间复杂度O(1)(n为主串长度,m为模式串长度)

解题思路二:KMP 算法(进阶线性解法)

核心原理:通过预处理模式串生成前缀数组,记录每个位置的最长相等前后缀长度。匹配失败时,无需窗口整体右移,利用前缀数组回退模式串匹配指针,避免重复字符比对,将时间复杂度优化至线性级别,适配超大字符串匹配场景。

复杂度分析:时间复杂度 O(n+m),空间复杂度 O(m)(存储前缀数组)

易错点总结:窗口滑动范围越界;部分字符匹配成功但整体未匹配,误返回下标;空串边界处理错误;KMP前缀数组构建逻辑出错。

示例测试:输入 haystack = "sadbutsad", needle = "sad",输出 0;输入 haystack = "leetcode", needle = "leeto",输出 -1

68. 文本左右对齐(困难)

题目简述:给定一个单词数组 words 和一个最大行宽度 maxWidth,重新排版文本,要求每行严格排版maxWidth 个字符,并遵循以下规则:1. 每行尽可能容纳更多单词,单词之间至少间隔一个空格;2. 非最后一行:单词左右对齐,多余空格均匀分配到单词间隙,左侧间隙空格数不少于右侧;3. 最后一行:单词左对齐,单词间仅保留单个空格,行尾补齐剩余空格;4. 单个单词独占一行时,直接左对齐,行尾补齐空格。

解题思路:分段模拟+精准空格分配(唯一标准解法)

核心原理:纯模拟字符串排版逻辑,分两步处理。第一步遍历单词,贪心分组,筛选出每行可容纳的所有单词;第二步分行处理空格,区分最后一行与普通行:普通行均匀分配多余空格,保证左右对齐;最后一行固定左对齐,统一补齐行尾空格,完全贴合题目格式要求。

解题步骤:1. 遍历单词数组,贪心分组,统计每行单词及基础字符长度;2. 区分当前行是否为最后一行;3. 非最后一行:计算总多余空格,平均分配至单词间隙,余数空格优先分配左侧间隙;4. 最后一行:单词单空格拼接,行尾补齐所有剩余空格;5. 保存所有行结果,最终返回排版数组。

复杂度分析:时间复杂度 O(n),空间复杂度 O(n)(n为所有单词字符总数)

易错点总结:多余空格分配不均匀,左右对齐格式错误;最后一行未单独处理,误做均分空格;单个单词行、双单词行边界处理遗漏;行尾空格补齐不完整。

示例测试:输入 words = ["This","is","an","example","of","text","justification."], maxWidth = 16,输出规范化对齐后的文本数组:["This is an","example of text","justification. "]

二、双指针专题

双指针是数组、字符串经典高效解题模板,通过两个指针同步遍历、相向遍历、快慢遍历,规避嵌套循环,将时间复杂度优化至 O(n),分为对撞指针、快慢指针、同步指针三大场景,适配绝大多数线性序列题型。

125. 验证回文串(简单)

题目简述:给定一个字符串 s,验证其是否为回文串。回文串指正读和反读完全相同的字符串。题目规则:仅考虑字母和数字字符,忽略大小写差异,忽略所有空格、标点等特殊符号。若字符串为空或仅含特殊字符,判定为有效回文串。

解题思路一:左右对撞指针(考场最优、O(1)空间模板解)

核心原理:定义左右指针分别从字符串首尾向中间相向遍历,原地跳过无效字符、统一大小写逐位比对,无需额外存储,时间空间最优,是面试唯一推荐写法。

解题步骤:1. 初始化左指针指向头部,右指针指向尾部;2. 左指针不断右移,跳过所有无效字符;3. 右指针不断左移,跳过所有无效字符;4. 两指针相遇前,比对两端有效字符,不一致直接返回 false;5. 全部字符匹配成功,返回 true。

复杂度:时间 O(n),空间 O(1)

优缺点:无额外空间、一趟遍历、边界严谨,无冗余操作,适配所有测试用例。

解题思路二:筛选+反转比对(朴素暴力解、好写易懂)

核心原理:先遍历字符串,筛选出所有字母、数字有效字符,统一转为小写生成新字符串;再将新字符串反转,对比原筛选字符串是否完全一致,一致则为回文串。

复杂度:时间 O(n),空间 O(n)

优缺点:逻辑极简、零出错风险,新手友好;但需要额外数组存储有效字符,空间开销更大,不适合极致优化场景。

示例测试:输入 s = "A man, a plan, a canal: Panama",输出 true;输入 s = "race a car",输出 false

392. 判断子序列(简单)

题目简述:给定两个字符串s(子串)和 t(母串),判断s 是否为 t 的子序列。子序列定义为:字符不改变相对顺序,无需连续,可通过删除母串部分字符(不改变剩余字符顺序)得到子串。

解题思路一:双指针贪心匹配(最优常规解)

核心原理:子母串双指针同步遍历,母串逐个匹配字符,匹配成功则子串指针后移,贪心消耗子串字符,一趟遍历完成匹配,效率最优。

解题步骤:1. 初始化子串指针 i、母串指针 j 均为 0;2. 双指针遍历母串,字符匹配成功则 i++;3. j 遍历结束后,判断 i 是否等于子串长度,相等则返回 true,否则 false。

复杂度:时间 O(n),空间 O(1)

解题思路二:动态规划预处理(进阶优化、适配海量查询)

核心原理:针对多次查询子序列的场景,预处理母串每个位置、每个字符的下一次出现位置,构建 dp 数组。后续查询子串时,直接跳跃匹配,单次查询 O(m),适合 s 多 t 固定的场景。

复杂度:预处理 O(n26),单次查询 O(m),空间 O(n26)

优缺点:单次查询不如双指针快,批量查询效率碾压双指针,是进阶面试考点。

易错点总结:子串遍历终止条件判断错误;颠倒母子串匹配逻辑;提前终止遍历导致漏匹配字符。

示例测试:输入 s = "abc", t = "ahbgdc",输出 true;输入 s = "axc", t = "ahbgdc",输出 false

167. 两数之和 II - 输入有序数组(中等)

题目简述:给定一个升序排列的整数数组 numbers 和目标数 target,请在数组中找出两个数,使两数之和等于 target。返回两数的下标(下标从 1 开始),题目保证有唯一解,且同一元素不能重复使用。依托数组有序特性,可实现零哈希表、常数空间求解。

解题思路一:有序数组对撞指针(最优空间解、面试首选)

核心原理:利用数组升序特性,左右对撞指针精准收缩区间。左指针最小值、右指针最大值,根据和与目标值的大小,单向收缩指针,零额外空间。

解题步骤:1. 初始化左指针 left = 0,右指针 right = 数组末尾下标;2. 循环计算两数之和,根据和与 target 的大小关系收缩指针;3. 匹配成功后,返回 [left+1, right+1](题目下标从1开始)。

复杂度:时间 O(n),空间 O(1)

解题思路二:二分查找(有序数组专属解法)

核心原理:遍历数组固定第一个数,剩余右侧有序区间二分查找补数(target - nums[i]),利用有序特性替代哈希表,空间 O(1)。

复杂度:时间 O(nlogn),空间 O(1)

优缺点:空间同样最优,时间略逊于双指针,适合练习二分模板。

解法三:哈希表法(通用两数之和解法、不依赖有序)

核心原理:空间换时间,遍历存储数值下标,查找补数,和普通两数之和逻辑一致,不利用数组有序特性。

复杂度:时间 O(n),空间 O(n)

易错点总结:返回下标未+1,不符合题目格式;指针收缩方向颠倒;重复使用同一数组元素。

示例测试:输入 numbers = [2,7,11,15], target = 9,输出 [1,2]

11. 盛最多水的容器(中等)

题目简述:给定长度为 n 的整数数组 height,每个元素代表竖直柱子的高度,柱子间距为 1。两根柱子与地面可围成一个容器,计算该容器可容纳的最大水量。容器水量由最短柱子高度两柱子间距共同决定,无需考虑柱子内部凹槽积水。

解题思路一:对撞指针+贪心优化(最优解、O(n))

核心原理:左右指针初始首尾最大化间距,每次移动短边指针,贪心获取更大高度可能性,必然能遍历到最优解,是本题唯一高效解法。

解题步骤:1. 初始化左右指针在首尾,记录最大水量为 0;2. 计算当前指针围成的水量,更新最大值;3. 对比左右柱子高度,移动短边指针;4. 指针相遇终止,返回最大水量。

复杂度:时间 O(n),空间 O(1)

解题思路二:暴力双层循环(朴素解法、超时)

核心原理:枚举所有两根柱子的组合,逐个计算水量,暴力更新最大值,逻辑直白但时间复杂度过高,无法通过大数据用例。

复杂度:时间 O(n²),空间 O(1)

易错点总结:错误移动长边指针,导致无法找到最优解;水量计算公式误用最大高度;未实时更新最大值。

示例测试:输入 height = [1,8,6,2,5,4,8,3,7],输出最大水量 49

15. 三数之和(中等)

题目简述:给你一个整数数组 nums,请你返回所有不重复的三元组 [nums[i], nums[j], nums[k]],满足三个数相加和为 0。要求:三元组不可重复、同一元素不能重复使用、返回结果内部无重复组合。

解题思路一:排序+固定数+对撞指针(标准最优解)

核心原理:排序去重,固定第一个数,剩余区间双指针相向查找,通过跳过重复元素彻底去重,将三层循环优化为 O(n²),考场必背模板。

解题步骤:1. 数组升序排序;2. 遍历固定首位元素,重复元素直接跳过;3. 首位元素右侧区间初始化左右指针,相向遍历;4. 根据三数之和大小收缩指针,匹配成功后记录结果并跳过重复元素;5. 遍历结束返回所有合法三元组。

复杂度:排序 O(nlogn),总时间 O(n²),空间 O(logn)

解题思路二:哈希表去重(另类解法、代码繁琐)

核心原理:固定前两个数,哈希表查找第三个数,通过集合去重避免重复三元组,无需双指针,但去重逻辑复杂、常数大,不推荐考场使用。

复杂度:时间 O(n²),空间 O(n)

解法三:暴力三重循环(超时、仅理解逻辑)

核心原理:枚举所有三元组组合,判断和是否为0,集合去重,时间复杂度极高,无法AC。

易错点总结:未做去重处理,出现重复结果;指针边界判断错误;固定数过大导致后续无匹配解未提前终止。

示例测试:输入 nums = [-1,0,1,2,-1,-4],输出 [[-1,-1,2],[-1,0,1]]

三、滑动窗口专题

滑动窗口是子数组、子串问题的核心模板,通过双指针维护窗口区间,右指针扩张窗口寻找合法区间,左指针收缩窗口优化最优解,分为不定长窗口、定长窗口两类,高效解决最值、匹配、统计类子区间问题。

209. 长度最小的子数组(中等)

题目简述:给定一个正整数数组 nums 和一个正整数 target,找出数组中满足和 ≥ target 的最短连续子数组,返回其长度。若不存在符合条件的子数组,返回 0。数组元素均为正数,无负数、零干扰,窗口单调性稳定。

解题思路一:不定长滑动窗口(最优模板、O(n))

核心原理:正数组窗口单调性稳定,右扩求和、达标左缩,实时更新最小窗口长度,一趟遍历完成。

解题步骤:1. 初始化左右指针、窗口和、最小长度;2. 右指针遍历数组,累加窗口内元素和;3. 窗口和达标后,循环收缩左边界,更新最小长度;4. 遍历结束,返回最小长度,无合法子数组则返回 0。

复杂度:时间 O(n),空间 O(1)

解题思路二:前缀和+二分查找(有序替代解法)

核心原理:构建前缀和数组(严格递增),遍历每个起点,二分查找最小合法终点,适配正数组特性,时间 O(nlogn)。

优缺点:可作为滑动窗口备用解法,适合无法使用双指针的变形题。

易错点总结:最小长度初始值设置不当;收缩窗口时机错误;未处理无合法子数组的边界情况。

示例测试:输入 target = 7, nums = [2,3,1,2,4,3],输出最小长度 2

3. 无重复字符的最长子串(中等)

题目简述:给定一个字符串 s,找出其中不含有重复字符的最长连续子串的长度。子串为连续字符序列,区别于非连续子序列,需保证窗口内所有字符唯一。

解题思路一:滑动窗口+哈希索引记录(最优万能解)

核心原理:哈希表存字符最新下标,重复则直接跳转左边界,动态维护无重复窗口,一趟遍历求最大值。

解题步骤:1. 初始化哈希表、左边界、最大长度;2. 右指针遍历字符串;3. 字符重复且重复下标在窗口内,更新左边界;4. 更新字符最新下标,计算窗口长度并更新最大值;5. 遍历结束返回结果。

复杂度:时间 O(n),空间 O(1)

解题思路二:滑动窗口+集合判重(朴素易懂解)

核心原理:哈希集合存储当前窗口字符,右指针遇重复字符,持续左移左边界并移除集合元素,直至窗口无重复,逻辑更直观。

优缺点:新手易理解,无下标计算易错点,效率略低于索引记录法。

解法三:暴力枚举(超时)

核心原理:枚举所有子串,集合判重统计最长长度,O(n²)超时,仅用于逻辑理解。

易错点总结:未判断重复字符是否在当前窗口内,错误更新左边界;左边界未取最大值,导致窗口回退。

示例测试:输入 s = "abcabcbb",输出 3;输入 s = "bbbbb",输出 1

30. 串联所有单词的子串(困难)

题目简述:给定一个字符串 s 和一个字符串数组 words,所有单词长度完全相同。找出字符串 s 中所有恰好串联所有单词一次的子串起始下标。子串需完整包含所有单词,无多余字符、无遗漏单词、无重复单词,单词顺序不限。

解题思路一:固定步长滑动窗口+频率哈希(标准最优解)

核心原理:利用单词定长特性,按偏移量分组滑动,实时统计窗口单词频率,匹配基准频率则记录下标。

解题步骤:1. 统计单词基准频率,计算单词长度、总匹配长度;2. 按单词长度遍历所有起始偏移量;3. 固定步长滑动窗口,截取单词统计实时频率;4. 频率匹配成功则记录下标,频率超标则收缩窗口;5. 遍历完成返回所有合法下标。

复杂度:时间 O(n×m),空间 O(m)

解题思路二:暴力枚举匹配(低效、可AC但不推荐)

核心原理:枚举所有可能的起始位置,截取固定长度子串,拆分单词后对比频率,暴力匹配,冗余计算多、效率低。

易错点总结:忽略单词长度固定特性,遍历逻辑冗余;频率统计未清零,导致窗口数据错乱;漏判单词数量匹配条件。

示例测试:输入 s = "barfoothefoobarman", words = ["foo","bar"],输出 [0,9]

76. 最小覆盖子串(困难)

题目简述:给你两个字符串 st,找出 s 中包含 t 的所有字符的最短连续子串,无匹配子串则返回空串。字符包含需满足数量匹配,若 t 中某字符出现多次,子串中对应字符数量必须不少于 t,是字符串滑动窗口经典万能模板。

解题思路一:频率计数滑动窗口(万能最优模板)

核心原理:双哈希表统计频率,右扩覆盖目标字符,左缩最小窗口,精准匹配字符种类与数量,所有字符串覆盖题通用。

解题步骤:1. 统计目标字符串字符频率;2. 右指针扩张窗口,累计窗口字符频率,统计已匹配字符种类;3. 完全匹配后,左指针收缩窗口,更新最小子串;4. 窗口不满足匹配条件时,再次右扩,循环至遍历结束。

复杂度:时间 O(n),空间 O(1)

解题思路二:暴力枚举子串(超时)

核心原理:枚举 s 所有子串,逐一校验是否覆盖 t 所有字符,记录最短长度,时间复杂度过高,仅逻辑学习。

易错点总结:仅匹配字符存在,未匹配字符数量;收缩窗口时机错误;最小窗口更新逻辑遗漏边界。

示例测试:输入 s = "ADOBECODEBANC", t = "ABC",输出 "BANC"

四、矩阵专题

矩阵题型核心为原地操作、边界模拟、状态标记,通过巧妙的空间复用、边界收缩、状态编码,规避额外二维数组开销,实现 O(1) 空间复杂度,核心考察细节模拟与规律总结能力。

36. 有效的数独(中等)

题目简述:判断一个 9×9 的数独是否有效。有效规则:1. 每行数字 1-9 不重复;2. 每列数字 1-9 不重复;3. 每个 3×3 九宫格内数字 1-9 不重复。仅校验已填充数字,空白格无需校验,无需判断数独是否有解。

解题思路一:数组哈希+一次遍历(最优常数解)

核心原理:三个二维数组分别标记行、列、九宫格数字状态,一次遍历完成所有重复校验,常数时间空间。

解题步骤:1. 初始化行、列、九宫格状态数组;2. 遍历9×9矩阵所有位置;3. 空白格直接跳过,计算当前九宫格下标;4. 校验数字重复状态,重复直接返回 false;5. 无重复则标记状态,遍历结束返回 true。

复杂度:时间 O(1),空间 O(1)

解题思路二:哈希集合分块校验(直观朴素解)

核心原理:分别遍历每一行、每一列、每个九宫格,用哈希集合判重,逻辑清晰,新手易懂,代码冗余但正确率高。

易错点总结:九宫格下标计算错误;重复校验顺序混乱;空白格误校验。

示例测试:输入合法9×9数独矩阵,输出 true;存在行列/九宫格重复数字,输出 false

54. 螺旋矩阵(中等)

题目简述:给定一个 m 行 n 列的二维矩阵,按照顺时针螺旋顺序,返回矩阵所有元素的遍历结果。遍历顺序:从左到右→从上到下→从右到左→从下到上,循环往复,逐层向内收缩,不可重复遍历元素。

解题思路一:四边界模拟收缩(标准考场模板)

核心原理:定义上下左右四边界,顺时针四向遍历,每轮遍历后收缩对应边界,边界交叉终止,无重复无遗漏。

解题步骤:1. 初始化上下左右边界;2. 左→右遍历上边界,收缩上边界;3. 上→下遍历右边界,收缩右边界;4. 右→左遍历下边界(边界合法时),收缩下边界;5. 下→上遍历左边界(边界合法时),收缩左边界;6. 边界交叉终止,返回遍历结果。

复杂度:时间 O(mn),空间 O(1)

解题思路二:方向数组+标记遍历(通用矩阵遍历解)

核心原理:定义右下左上四个方向数组,遍历过程中预判下一个位置是否越界或已访问,满足条件则切换方向,通过 visited 数组标记已访问节点。

优缺点:逻辑通用,适配各类矩阵遍历题型;需要额外 visited 数组,空间 O(mn)。

易错点总结:边界收缩顺序错误;未判断边界合法性,导致重复遍历、越界;单层遍历终止条件错误。

示例测试:输入 matrix = [[1,2,3],[4,5,6],[7,8,9]],输出 [1,2,3,6,9,8,7,4,5]

48. 旋转图像(中等)

题目简述:给定一个 n×n 的二维矩阵,将矩阵顺时针旋转 90 度,要求原地修改矩阵,不可使用额外矩阵空间存储结果,仅可使用常数额外空间。

解题思路一:转置+行反转(O(1)最优原地模板)

核心原理:顺时针90度固定规律:主对角线转置 + 每行反转,全程原地交换,无额外空间,背诵即用。

解题步骤:1. 遍历矩阵上三角区域,完成矩阵转置;2. 逐行遍历矩阵,反转每行所有元素;3. 无需返回,原地修改完成旋转。

复杂度:时间 O(n²),空间 O(1)

解题思路二:四角循环交换(纯原地旋转、无转换)

核心原理:直接枚举每个旋转区块,一次性交换四个对应位置的元素,直接完成旋转,无需转置和反转,纯数学位置映射。

优缺点:同样 O(1) 空间,无需两次遍历,但位置下标计算复杂,易错,不推荐考场使用。

易错点总结:转置遍历区间错误,导致元素重复交换;反转列顺序颠倒;混淆顺时针、逆时针旋转规律。

示例测试:输入 [[1,2,3],[4,5,6],[7,8,9]],旋转后 [[7,4,1],[8,5,2],[9,6,3]]

73. 矩阵置零(中等)

题目简述:给定一个 m×n 矩阵,若某个元素为 0,则将其所在整行、整列所有元素置为 0。要求原地修改矩阵,尽可能降低空间复杂度,最优解不使用额外数组,复用矩阵首行首列做标记。

解题思路一:首行首列原地标记(最优O(1)空间解)

核心原理:复用原矩阵首行首列作为标记数组,仅用两个变量记录首行首列原始零状态,无额外空间,原地完成标记与置零。

解题步骤:1. 单独标记首行、首列是否存在0;2. 遍历剩余矩阵,用首行首列标记行列零元素;3. 根据首行首列标记,将对应行列全部置零;4. 最后根据初始标记,处理首行、首列置零。

复杂度:时间 O(mn),空间 O(1)

解题思路二:额外数组标记(朴素易懂解)

核心原理:新开两个一维数组,分别标记每行、每列是否需要置零,遍历标记后批量置零,逻辑零出错。

复杂度:时间 O(mn),空间 O(m+n)

易错点总结:优先修改首行首列,导致标记被覆盖;未单独处理首行首列初始零元素,结果出错;置零顺序颠倒。

示例测试:输入 [[1,1,1],[1,0,1],[1,1,1]],输出 [[1,0,1],[0,0,0],[1,0,1]]

289. 生命游戏(中等)

题目简述:给定 m×n 二维网格,每个格子代表一个细胞,细胞状态:1为活细胞,0为死细胞。根据四条规则更新细胞状态:1. 活细胞周围少于2个活邻居→死亡;2. 活细胞周围2-3个活邻居→存活;3. 活细胞周围多于3个活邻居→死亡;4. 死细胞周围恰好3个活邻居→复活。所有细胞同时更新状态,要求原地修改、常数空间。

解题思路一:复合状态编码(最优O(1)原地解)

核心原理:自定义过渡状态编码(0死→死、1活→活、2活→死、3死→活),先标记所有过渡状态,再统一解码,保证所有细胞同步更新。

解题步骤:1. 遍历网格每个细胞,统计8方位活邻居数量;2. 根据规则标记复合过渡状态;3. 二次遍历网格,将复合状态解码为最终生死状态;4. 原地更新完成迭代。

复杂度:时间 O(mn),空间 O(1)

解题思路二:额外矩阵复制(朴素模拟解)

核心原理:复制一份原始网格作为参照,基于原网格数据统计邻居、更新新网格状态,保证同步更新,逻辑简单无bug。

复杂度:时间 O(mn),空间 O(mn)

易错点总结:直接即时更新状态,干扰后续细胞统计;8方位边界判断遗漏;复合状态编码、解码规则混淆。

示例测试:输入 [[0,1,0],[0,0,1],[1,1,1],[0,0,0]],输出 [[0,0,0],[1,0,1],[0,1,1],[0,1,0]]

五、哈希表专题

哈希表是刷题核心高频工具,核心价值为空间换时间,可将查找、匹配、统计操作从 O(n) 优化至 O(1)。常用于字符频率统计、映射关系匹配、元素去重判环、区间最值查找等场景。字符类题目优先用数组替代哈希表,效率更高,复杂映射场景使用哈希表。

383. 赎金信(简单)

题目简述:给定两个字符串 ransomNote(赎金信)和 magazine(杂志),判断能否利用杂志中的字符构造出赎金信。每个字符仅可使用一次,杂志字符数量必须大于等于赎金信所需字符数量。

解题思路一:字母数组计数(最优常数空间解)

核心原理:小写字母固定26位,用数组替代哈希表,统计杂志字符频率,递减赎金信字符计数,判断是否充足,效率最高。

解题步骤:1. 初始化26位字母计数数组,初始值全为0;2. 遍历杂志字符串,统计所有字符频率;3. 遍历赎金信字符串,对应字符计数递减;4. 若任意字符计数小于0,返回false;遍历完成无异常,返回true。

复杂度:时间 O(m+n),空间 O(1)

解题思路二:HashMap哈希统计(通用字符串解法)

核心原理:哈希表统计杂志字符频次,遍历赎金信扣减频次,逻辑通用,适配大小写、特殊字符等复杂场景。

复杂度:时间 O(m+n),空间 O(26)

易错点总结:使用哈希表冗余操作,效率偏低;未判断字符计数负数边界;颠倒赎金信与杂志统计逻辑。

示例测试:输入 ransomNote = "a", magazine = "b",输出 false;输入 ransomNote = "aa", magazine = "aab",输出 true

205. 同构字符串(简单)

题目简述:给定两个字符串 st,判断两个字符串是否同构。同构定义:字符串字符可以通过固定映射规则相互转换,字符一对一映射,不可多对一、一对多,字符顺序保持不变。

解题思路一:双向哈希映射(标准唯一正确解)

核心原理:构建 s→t、t→s 双向映射,杜绝多对一、一对多漏洞,是同构类题目通用标准答案。

解题步骤:1. 初始化两个哈希表存储双向字符映射;2. 同步遍历两个字符串对应位置字符;3. 校验双向映射,存在冲突直接返回false;4. 无冲突则更新映射关系,遍历完成返回true。

复杂度:时间 O(n),空间 O(1)

解题思路二:首次索引序列映射(极简另类解)

核心原理:记录每个字符第一次出现的下标,生成两个字符串的索引序列,序列完全一致则同构,无需映射表。

示例:egg→[0,1,0],add→[0,1,0],序列相同判定同构。

易错点总结:仅做单向映射,无法拦截多对一错误;映射覆盖更新导致逻辑错误;字符串长度不一致未提前判空。

示例测试:输入 s = "egg", t = "add",输出 true;输入 s = "foo", t = "bar",输出 false

290. 单词规律(简单)

题目简述:给定模式字符串 pattern 和字符串 s,判断 s 是否遵循相同的单词规律。pattern 中单个字符对应 s 中单个单词,严格一对一映射,不可重复对应、错乱匹配。

解题思路一:字符-单词双向映射(同构通用模板)

核心原理:字符串同构变形题,映射双方变为字符与单词,双向哈希校验一一对应,逻辑完全复用205题模板。

解题步骤:1. 将字符串 s 按空格分割为单词数组;2. 校验模式长度与单词数组长度是否一致,不一致直接返回false;3. 构建字符→单词、单词→字符双向映射;4. 逐位遍历校验映射关系,冲突则返回false,遍历完成返回true。

复杂度:时间 O(n),空间 O(n)

解题思路二:序列编码匹配(极简解法)

核心原理:分别对 pattern 字符、s 单词生成首次出现编码序列,对比两组序列是否完全一致,无需双向映射。

易错点总结:未校验长度一致性导致越界;单向映射出现多对一漏洞;分割字符串遗漏连续空格问题。

示例测试:输入 pattern = "abba", s = "dog cat cat dog",输出 true;输入 pattern = "abba", s = "dog cat cat fish",输出 false

242. 有效的字母异位词(简单)

题目简述:给定两个字符串 st,判断 t 是否为 s 的字母异位词。字母异位词定义:两个字符串包含的字符种类、数量完全一致,仅字符排列顺序不同。

解题思路一:字母数组计数(最优极简解)

核心原理:26位字母数组统计频次,一增一减,最终全零即为异位词,常数空间、效率最高。

解题步骤:1. 提前判断两字符串长度,不一致直接返回false;2. 初始化26位计数数组;3. 遍历s累加字符频率,遍历t递减字符频率;4. 遍历计数数组,存在非0值则返回false,否则返回true。

复杂度:时间 O(n),空间 O(1)

解题思路二:字符串排序比对(无脑简单解)

核心原理:两个字符串分别排序,排序后完全一致则为异位词,代码极简,无需统计频次。

复杂度:时间 O(nlogn),空间 O(n)

解法三:HashMap哈希统计(通用复杂字符解)

核心原理:哈希表统计字符频次,适配含大小写、特殊字符的复杂场景,通用性强。

易错点总结:未提前校验长度,浪费计算资源;使用哈希表替代数组,冗余低效;仅判断部分字符,未全局校验数组。

示例测试:输入 s = "anagram", t = "nagaram",输出 true;输入 s = "rat", t = "car",输出 false

49. 字母异位词分组(中等)

题目简述:给定字符串数组,将所有字母异位词分为同一组,返回分组后的结果。异位词要求字符种类、数量完全一致,顺序不同,所有异位词归为同一分组。

解题思路一:排序建键+哈希分组(考场首选模板)

核心原理:异位词排序后字符串唯一,以排序串为键、原字符串数组为值,一键分组,代码简洁、零出错。

解题步骤:1. 初始化哈希表,键为排序字符串,值为字符串数组;2. 遍历数组每个字符串,对字符排序生成唯一键;3. 将原字符串存入对应键的分组中;4. 遍历完成后,取出哈希表所有值作为结果返回。

复杂度:时间 O(nklogk),空间 O(nk)

解题思路二:字符频次编码建键(高效进阶解)

核心原理:用26位字母频次拼接字符串作为唯一键,无需排序,时间复杂度优化为 O(nk),效率更高,适合长字符串。

解法三:质数乘积哈希(数学最优解)

核心原理:26个字母对应26个质数,字符串所有字符质数乘积作为唯一哈希键,异位词乘积必然相等,无排序开销,但大数易溢出。

易错点总结:键值对应错误,分组错乱;未新建数组直接引用,导致数据覆盖;排序逻辑出错。

示例测试:输入 strs = ["eat","tea","tan","ate","nat","bat"],输出 [["bat"],["nat","tan"],["ate","eat","tea"]]

1. 两数之和(简单)

题目简述:给定整数数组 nums 和目标值 target,在数组中找出和为目标值的两个整数,返回它们的数组下标。数组中仅有唯一解,同一元素不可重复使用。

解题思路一:一趟哈希表(最优O(n)解)

核心原理:边遍历边存储数值下标,查找补数,一趟遍历完成,时间最优。

解题步骤:1. 初始化空哈希表存储数值与下标;2. 遍历数组每个元素,计算所需补数;3. 补数存在则直接返回 [补数下标, 当前下标];4. 补数不存在则存入当前数据,继续遍历。

复杂度:时间 O(n),空间 O(n)

解题思路二:暴力双层循环(朴素超时解)

核心原理:枚举所有两个数的组合,判断和是否为target,逻辑简单,O(n²)超时。

解法三:排序+双指针(无下标场景可用)

核心原理:数组排序后双指针查找数值,缺点是排序会打乱原下标,需要额外存储原下标,代码繁琐。

易错点总结:先存数据再判断,导致自身匹配;暴力双层循环效率过低;下标对应错乱。

示例测试:输入 nums = [2,7,11,15], target = 9,输出 [0,1]

202. 快乐数(简单)

题目简述:编写算法判断一个数是否为快乐数。快乐数定义:反复将数字替换为各位数字的平方和,若最终变为1则为快乐数;若进入循环、重复出现数字且不为1,则不是快乐数。

解题思路一:哈希集合判重(直观易懂解)

核心原理:记录所有出现过的数字,重复则循环无解,等于1则为快乐数,逻辑直白。

解题步骤:1. 初始化哈希表记录已出现数字;2. 循环计算当前数的各位平方和;3. 结果为1,返回true;4. 结果已存在哈希表,存在循环返回false;5. 无重复则记录数字,继续迭代。

复杂度:时间 O(logn),空间 O(logn)

解题思路二:快慢指针判环(O(1)空间最优解)

核心原理:将平方和迭代视为链表遍历,快慢指针追击,相遇则有环(非快乐数),快指针到1则为快乐数,零额外空间。

易错点总结:平方和计算逻辑错误;未判重导致死循环;终止条件判断颠倒。

示例测试:输入 n = 19,输出 true;输入 n = 2,输出 false

219. 存在重复元素 II(简单)

题目简述:给定整数数组 nums 和整数 k,判断数组中是否存在两个相同元素,且两个元素的下标间距绝对值不大于 k。数组可无序,只需满足数值相等、下标距离合规即可。

解题思路一:哈希表存最新索引(考场最优、一趟遍历)

核心思路:哈希表记录每个数字最新出现的下标,避免旧下标干扰,遍历过程中实时判断相邻重复元素的距离是否 ≤k,一趟遍历完成判断。

解题步骤:1. 初始化空哈希表,存储「数值-最新下标」映射关系;2. 遍历数组每一个元素,若当前数值已存在于哈希表;3. 计算当前下标与存储的最新下标差值,绝对值≤k则直接返回 true;4. 无论是否匹配成功,更新当前数值的最新下标;5. 遍历结束未找到合规元素,返回 false。

复杂度:时间 O(n),空间 O(n)

优缺点:仅遍历一次数组,效率极高,逻辑简洁;需额外哈希空间存储下标,是时间最优解。

解题思路二:滑动窗口+哈希集合(窗口统计解法)

核心思路:维护长度为 k 的固定滑动窗口,哈希集合存储窗口内元素,保证窗口内所有元素下标间距合规,新元素重复即满足题意,超出窗口范围则移除旧元素。

解题步骤:1. 初始化空哈希集合作为滑动窗口;2. 遍历数组元素,当前元素存在于集合中则返回 true;3. 不存在则将元素加入集合;4. 当窗口长度超过 k 时,移除窗口最左侧过期元素;5. 遍历结束无重复,返回 false。

复杂度:时间 O(n),空间 O(k)

优缺点:空间复杂度更低,仅维护固定大小窗口;适合 k 远小于数组长度的场景,是空间优化解法。

易错点总结:未更新最新下标,使用旧下标判断导致结果错误;滑动窗口移除元素时机错误;差值判断符号出错。

示例测试:输入 nums = [1,2,3,1], k = 3,输出 true;输入 nums = [1,2,3,1,2,3], k = 2,输出 false

128. 最长连续序列(中等)

题目简述:给定未排序的整数数组,找出数字最长连续序列的长度。连续序列仅要求数值连续,不要求数组下标连续,题目进阶要求时间复杂度 O(n)。

解题思路一:哈希集合+起点遍历(O(n)最优解、核心模板)

核心思路:哈希集合数组去重,仅遍历序列起点元素(不存在 num-1 的元素),从起点向右侧连续扩展统计长度,跳过所有非起点元素,避免重复计算,保证线性时间复杂度。

解题步骤:1. 将数组转为哈希集合,完成去重,规避重复数值干扰;2. 遍历集合中每一个数字,判断是否为连续序列起点(集合无 num-1);3. 若为起点,循环累加 num+1,统计当前连续序列长度;4. 实时更新全局最大长度,遍历结束返回结果。

复杂度:时间 O(n),空间 O(n)

优缺点:严格满足题目 O(n) 时间要求,无冗余遍历,面试最优标准答案;需要哈希集合存储元素。

解题思路二:数组排序+单次遍历(朴素易懂解)

核心思路:先对数组排序并去重,有序数组下直接遍历,统计相邻数值连续的最长长度,逻辑简单、新手友好。

复杂度:时间 O(nlogn),空间 O(logn)

优缺点:无需哈希表,空间开销小;排序耗时高,不满足题目 O(n) 进阶要求,仅适合基础解题。

解法三:哈希表双向扩展(进阶拓展解法)

核心思路:哈希表存储每个数值对应的连续序列长度,遍历元素时向左右双向扩展,更新边界数值的长度,高效统计连续区间。

复杂度:时间 O(n),空间 O(n)

优缺点:同样线性时间,可记录区间边界,适配需要输出具体序列的变形题。

易错点总结:未去重导致重复统计;未判断起点,遍历所有元素造成超时;连续长度更新逻辑错误。

示例测试:输入 nums = [100,4,200,1,3,2],输出 4(最长序列 1,2,3,4)

六、区间专题

区间专题是算法高频贪心题型,核心解题逻辑:先排序、再遍历合并/筛选,通过排序规整区间顺序,再通过一次遍历完成区间合并、插入、筛选、统计,所有区间题通用预处理技巧为端点排序。

228. 汇总区间(简单)

题目简述:给定一个无重复、有序升序的整数数组 nums,将连续的数字区间汇总为字符串。单数字区间直接输出数字,连续多数字区间输出 start->end,返回所有区间字符串集合。

解题思路一:一次遍历分段统计(最优模板解)

核心思路:连续序列分组模板,遍历数组维护区间起点,当当前数字与下一个数字不连续时,闭合当前区间,生成对应区间字符串,开启新区间。

解题步骤:1. 数组为空直接返回空集合;2. 初始化区间起点为数组首个元素;3. 遍历数组,判断当前元素与下一元素是否连续;4. 不连续则闭合区间,单值直接存入,多值拼接 a->b;5. 更新起点为下一元素,遍历结束处理最后一个区间。

复杂度:时间 O(n),空间 O(1)

优缺点:一趟遍历完成,无冗余操作,是本题标准考场模板。

解题思路二:双指针区间锁定(朴素清晰解)

核心思路:快慢双指针锁定连续区间,慢指针为区间起点,快指针向右遍历至不连续位置,截取区间生成字符串,迭代完成所有区间统计。

复杂度:时间 O(n),空间 O(1)

优缺点:逻辑分层清晰,边界处理更直观,适合新手理解区间分组逻辑。

易错点总结:遗漏处理数组最后一个区间;连续判断条件错误;单数字区间格式拼接错误。

示例测试:输入 nums = [0,1,2,4,5,7],输出 ["0->2","4->5","7"]

56. 合并区间(中等)

题目简述:给定若干区间数组,合并所有重叠或相邻的区间,返回无重叠、有序的全新区间集合。区间无序,重叠、相邻、包含关系均需要合并。

解题思路一:左端点排序+贪心遍历(通用最优解)

核心思路:区间问题通用第一步:按区间左端点升序排序,保证区间有序;再贪心遍历,逐个对比当前区间与结果集最后区间,重叠则合并,不重叠则直接加入。

解题步骤:1. 特判空数组,直接返回空;2. 按区间左端点升序排序所有区间;3. 初始化结果集,存入第一个区间;4. 遍历剩余区间,对比当前区间与结果集末尾区间;5. 左端点≤末尾区间右端点,判定重叠,更新末尾区间右端点为最大值;6. 无重叠则直接加入结果集,遍历结束返回结果。

复杂度:排序 O(nlogn),遍历 O(n),总时间 O(nlogn),空间 O(logn)

优缺点:所有区间合并题通用模板,逻辑严谨、无遗漏,面试必背。

解题思路二:暴力两两合并(朴素低效解)

核心思路:不排序,双重循环枚举所有区间,两两判断重叠并合并,重复迭代直至无重叠区间。

复杂度:时间 O(n²),空间 O(n)

优缺点:无需排序,逻辑直白;时间复杂度过高,大数据用例超时,仅适合逻辑理解。

易错点总结:未排序直接合并导致遗漏重叠区间;合并时未取右端点最大值;相邻区间未合并。

示例测试:输入 intervals = [[1,3],[2,6],[8,10],[15,18]],输出 [[1,6],[8,10],[15,18]]

57. 插入区间(中等)

题目简述:给定已按左端点排序、无重叠的区间数组,以及一个新的待插入区间,将新区间插入并合并重叠区间,返回最终有序无重叠区间,要求尽量高效、无需二次全排序。

解题思路一:三段式拼接(最优进阶解、无需排序)

核心思路:将所有区间分为三段:左侧无重叠区间、中间可合并重叠区间、右侧无重叠区间,分别遍历筛选,合并中间区间后直接三段拼接,全程无需排序。

解题步骤:1. 遍历筛选出所有右端点<新区间左端点的左侧无重叠区间;2. 遍历所有剩余重叠区间,不断更新新区间的左右边界,完成合并;3. 筛选出所有左端点>新区间右端点的右侧无重叠区间;4. 左侧区间 + 合并后新区间 + 右侧区间,拼接为最终结果。

复杂度:时间 O(n),空间 O(n)

优缺点:利用原数组有序特性,规避排序开销,时间最优,是本题专属最优解法。

解题思路二:暴力插入后合并(朴素无脑解)

核心思路:直接将新区间加入原区间数组,复用56题合并区间模板,排序后统一合并。

复杂度:时间 O(nlogn),空间 O(n)

优缺点:代码极简、零思维成本;浪费有序特性,存在冗余排序,效率偏低。

易错点总结:区间边界判断错误,漏合并重叠区间;三段划分逻辑混乱;新区间边界更新不彻底。

示例测试:输入 intervals = [[1,3],[6,9]], newInterval = [2,5],输出 [[1,5],[6,9]]

452. 最少箭引爆气球(中等)

题目简述:给定若干气球水平区间,一支箭垂直射出可击穿所有覆盖区间的气球,求引爆所有气球需要的最少弓箭数量。核心贪心逻辑:一支箭尽可能击穿最多气球。

解题思路一:右端点排序+贪心选取(经典最优解)

核心思路:区间贪心经典技巧,按区间右端点升序排序,每次贪心选择当前最右侧端点射箭,覆盖尽可能多的重叠气球,每选一次弓箭数+1。

解题步骤:1. 特判空数组返回0;2. 按气球区间右端点升序排序;3. 初始化弓箭数为1,记录首个区间右端点为射箭位置;4. 遍历后续区间,若当前区间左端点>射箭位置,无重叠需新增弓箭,更新射箭位置;5. 遍历结束返回弓箭总数。

复杂度:时间 O(nlogn),空间 O(logn)

优缺点:贪心策略最优,弓箭数量最少,是本题唯一标准答案,区间贪心模板题。

解题思路二:左端点排序(易错备用解)

核心思路:按左端点排序,动态更新重叠区间的最小右端点,判断是否需要新增弓箭。

复杂度:时间 O(nlogn),空间 O(logn)

优缺点:可解题,但边界判断繁琐、极易出错,不推荐考场使用。

易错点总结:错误使用左端点贪心,导致弓箭数增多;重叠区间判断条件颠倒;射箭位置更新错误。

示例测试:输入 points = [[10,16],[2,8],[1,6],[7,12]],输出 2

七、栈专题

栈是**后进先出(LIFO)**线性结构,核心适配场景:括号匹配、路径简化、表达式求值、最小值缓存、嵌套结构解析,所有题型均可通过栈模拟递归、缓存状态,实现线性时间求解。

20. 有效的括号(简单)

题目简述:给定只包含 ()[]{}, 的字符串,判断括号是否有效。有效规则:左右括号完全匹配、正确嵌套、顺序合法,每个右括号必须有对应同类型左括号。

解题思路一:栈+哈希映射(通用模板解)

核心思路:栈匹配经典模板,左括号入栈,遇到右括号匹配栈顶元素,哈希表存储右括号对应左括号,快速校验匹配关系,最终栈空则完全合法。

解题步骤:1. 构建哈希表,键为右括号、值为对应左括号;2. 遍历字符串所有字符;3. 左括号直接入栈;4. 右括号则判断栈是否为空、栈顶是否匹配,不匹配直接返回 false;5. 匹配成功则栈顶出栈,遍历结束判断栈是否为空,空则返回 true。

复杂度:时间 O(n),空间 O(n)

优缺点:适配所有括号类型,逻辑通用、边界严谨,面试标配解法。

解题思路二:字符串循环替换(朴素暴力解)

核心思路:循环替换字符串中所有合法成对括号,直至无法替换,最终字符串为空则有效。

复杂度:时间 O(n²),空间 O(n)

优缺点:代码极简无需栈结构;效率极低,大数据超时,仅新手练习使用。

易错点总结:先出现右括号栈空未判错;括号类型匹配错误;遍历结束未校验栈是否为空。

示例测试:输入 s = "()[]{}",输出 true;输入 s = "(]",输出 false

71. 简化路径(中等)

题目简述:给定 Unix 风格文件路径,根据规则简化为标准路径。规则:. 代表当前目录、.. 返回上一级目录、多个连续斜杠等价于单个斜杠,最终路径无多余斜杠、无无效目录。

解题思路一:字符串分割+栈模拟(标准最优解)

核心思路:字符串处理+栈经典技巧,分割路径过滤无效字符,栈存储有效目录名,遇到 .. 弹出栈顶(返回上级),其余有效目录入栈,最后拼接标准路径。

解题步骤:1. 以斜杠 / 分割原路径,得到目录数组;2. 遍历分割结果,过滤空字符、. 无效目录;3. 遍历到 .. 且栈不为空,弹出栈顶目录;4. 普通有效目录直接入栈;5. 遍历结束,将栈内目录拼接为标准绝对路径。

复杂度:时间 O(n),空间 O(n)

优缺点:逻辑清晰、规则全覆盖,无边界漏洞,是本题唯一通用解法。

解题思路二:正则匹配替换(极简偷懒解)

核心思路:通过正则匹配替换连续斜杠、当前目录,再循环处理上级目录,简化路径。

复杂度:时间 O(n),空间 O(n)

优缺点:代码简短;依赖正则语法,面试不推荐手写,不利于算法能力考察。

易错点总结:栈为空时仍执行 .. 出栈操作;未过滤空字符串导致多余斜杠;路径拼接格式错误。

示例测试:输入 path = "/home//foo/",输出 "/home/foo";输入 path = "/a/./b/../../c/",输出 "/c"

155. 最小栈(中等)

题目简述:设计一个栈结构,支持入栈、出栈、取栈顶、获取最小值操作,要求获取最小值 O(1) 时间复杂度

解题思路一:双栈同步存储(考场最优模板)

核心思路:主栈存储所有元素,辅助最小值栈同步存储对应位置的最小值,双栈同步入栈、同步出栈,永久保证辅助栈栈顶为当前全局最小值,实现 O(1) 取最小。

解题步骤:1. 初始化主栈、最小辅助栈;2. 入栈:主栈正常入值,辅助栈入当前最小值(新值与栈顶最小值更小者);3. 出栈:双栈同步弹出栈顶元素;4. 取最小值:直接返回辅助栈栈顶元素。

复杂度:所有操作 O(1),空间 O(n)

优缺点:逻辑零漏洞、无边界错误,代码好写、面试首选,是标准工程解法。

解题思路二:单栈差值存储(空间优化进阶解)

核心思路:仅用单个栈存储元素与当前最小值的差值,单独变量记录全局最小值,规避辅助栈开销,实现 O(1) 空间优化。

复杂度:所有操作 O(1),空间 O(1)

优缺点:空间极致优化;差值计算逻辑复杂、正负边界易错,新手不推荐。

易错点总结:辅助栈入栈规则错误;出栈不同步导致最小值错乱;单栈差值正负判断失误。

示例测试:依次入栈[-2,0,-3],最小值为-3;出栈-3后,最小值为-2

150. 逆波兰表达式求值(中等)

题目简述:根据逆波兰表达式(后缀表达式)计算结果,表达式仅包含整数和 +、-、*、/,除法向零取整,无括号,严格遵循后缀运算规则。

解题思路一:栈模拟运算(后缀表达式万能模板)

核心思路:后缀表达式标准解法,数字入栈,遇到运算符弹出栈顶两个数字计算,先弹右操作数、后弹左操作数,计算结果重新入栈,最终栈顶为答案。

解题步骤:1. 初始化数字栈;2. 遍历表达式所有字符;3. 数字直接转为整数入栈;4. 运算符依次弹出两个数,按规则运算,结果入栈;5. 遍历结束,栈中唯一元素即为最终结果。

复杂度:时间 O(n),空间 O(n)

优缺点:所有后缀表达式通用解法,逻辑固定、无歧义,算法必考模板。

解题思路二:递归求解(进阶思路解)

核心思路:逆波兰表达式天然适配递归,从后向前遍历,运算符左右递归求解子表达式,合并结果。

复杂度:时间 O(n),空间 O(n)

优缺点:思路巧妙;递归栈开销大,代码繁琐,不适合考场快速手写。

易错点总结:操作数顺序颠倒(减法、除法不可逆);除法未向零取整;字符数字转换错误。

示例测试:输入 tokens = ["2","1","+","3","*"],输出 9

224. 基本计算器(困难)

题目简述:实现基础计算器,支持字符串表达式的 +、- 运算、括号嵌套、正负号、空格过滤,是表达式计算经典难题。

解题思路一:栈存符号+结果(通用迭代解法)

核心思路:表达式计算通用解法,栈缓存括号前的计算结果与符号,遍历过程中实时计算数值、记录正负符号,遇到括号缓存状态,出括号后回溯计算,处理所有嵌套场景。

解题步骤:1. 初始化结果、当前符号、数字缓存、符号栈;2. 遍历字符串,过滤空格;3. 拼接连续数字,解析完整数值;4. 遇到 +、-,累加当前结果,更新符号;5. 遇到左括号,缓存当前结果和符号,重置计算状态;6. 遇到右括号,弹出栈顶状态,合并括号内结果;7. 遍历结束累加最后一组数值,返回结果。

复杂度:时间 O(n),空间 O(n)

优缺点:迭代无递归栈溢出风险,全覆盖括号、正负号、空格场景,工业级通用解法。

解题思路二:递归拆解表达式(直观递归解)

核心思路:遇到左括号递归求解子表达式,右括号终止递归,逐层返回计算结果,贴合人类计算思维。

复杂度:时间 O(n),空间 O(n)

优缺点:逻辑易懂;递归深度过大易栈溢出,边界处理繁琐。

易错点总结:正负号处理错误;连续多位数解析不全;括号嵌套状态缓存错乱;空格未过滤导致解析失败。

示例测试:输入 s = "(1+(4+5+2)-3)+(6+8)",输出 23

八、链表专题

链表是高频面试核心题型,区别于数组,链表无法随机访问,核心解题技巧:虚拟头节点、快慢指针、原地反转、断链重连、原地复制、双链表拼接。绝大多数链表题可通过迭代/递归双解法求解,重点考察指针操作、边界处理、原地优化思维。

141. 环形链表(简单)

题目简述:给定一个链表头节点,判断链表中是否存在环。若链表存在环,返回 true;无环则返回 false。不允许修改原链表结构,进阶要求空间 O(1)。

解题思路一:快慢指针(龟兔赛跑、O(1)最优解)

核心思路:链表判环最优解,慢指针每次走1步,快指针每次走2步,若存在环,快慢指针必然在环内相遇;若快指针走到末尾为空,说明无环。

解题步骤:1. 特判链表为空或单个节点,直接返回 false;2. 初始化慢指针、快指针均指向头节点;3. 循环遍历,慢指针走一步,快指针走两步;4. 快慢指针相遇则存在环,返回 true;5. 快指针为空或下一节点为空,遍历结束返回 false。

复杂度:时间 O(n),空间 O(1)

优缺点:常数空间、无额外开销,考场最优模板,所有链表判环题通用。

解题思路二:哈希集合判重(朴素直观解)

核心思路:哈希集合记录所有遍历过的节点,遍历过程中重复访问节点则存在环,走到末尾则无环。

复杂度:时间 O(n),空间 O(n)

优缺点:逻辑简单、零出错;占用额外空间,不满足进阶优化要求。

易错点总结:快指针循环终止条件写错导致空指针报错;快慢指针步长设置错误;起始指针位置错误。

示例测试:链表 [3,2,0,-4] 尾节点指向第二个节点,输出 true;无环链表输出 false

2. 两数相加(中等)

题目简述:给定两个逆序存储数字的链表,每个节点存储单个数字,将两数相加,返回同样逆序存储结果的新链表。需全程处理进位,链表长度可不一致。

解题思路一:虚拟头节点+迭代遍历(通用模板解)

核心思路:链表加法标准模板,创建虚拟头节点统一链表操作,同步遍历两个链表,逐位相加、记录进位,遍历结束处理最后剩余进位。

解题步骤:1. 初始化虚拟头节点、遍历指针、进位变量;2. 两链表任一不为空或存在进位则持续循环;3. 取出当前节点数值,为空则取0;4. 计算总和与新进位,创建新节点存储个位数值;5. 指针后移,循环结束返回虚拟头节点下一节点。

复杂度:时间 O(max(n,m)),空间 O(1)

优缺点:逻辑严谨、边界全覆盖,长短链表、末尾进位场景无漏洞,面试首选模板。

解题思路二:递归求解(极简进阶解)

核心思路:递归逐位相加,将进位带入下一层递归,无需手动循环,代码极简。

复杂度:时间 O(max(n,m)),空间 O(max(n,m))

优缺点:代码简短优雅;递归深度取决于链表长度,长链表存在栈开销。

易错点总结:遗漏最后一位进位;链表长度不一致未判空;节点拼接错乱。

示例测试:l1 = [2,4,3], l2 = [5,6,4],输出 [7,0,8](342+465=807)

21. 合并两个有序链表(简单)

题目简述:给定两个升序有序链表,合并为一个全新的升序有序链表,返回合并后链表头节点,可复用原链表节点。

解题思路一:迭代+虚拟头节点(省空间通用解)

核心思路:虚拟头节点统一操作,双指针遍历两个链表,每次选取更小节点拼接至结果链表,遍历完成拼接剩余尾部节点,空间最优。

解题步骤:1. 创建虚拟头节点,定义遍历指针;2. 两链表均不为空时,比较节点值;3. 将更小节点接入结果链表,对应链表指针后移;4. 任一链表遍历完毕,直接拼接另一链表剩余节点;5. 返回虚拟头节点下一节点。

复杂度:时间 O(n+m),空间 O(1)

优缺点:原地复用节点、无额外空间开销,稳定性强,适合工程场景。

解题思路二:递归拼接(极简代码解)

核心思路:递归比较两个链表头节点,将较小节点的 next 指向后续递归结果,逐层回溯完成合并。

复杂度:时间 O(n+m),空间 O(n+m)

优缺点:代码极简、三行核心代码,背诵成本极低;递归栈占用空间,不适合超长链表。

易错点总结:剩余节点未拼接导致链表断裂;节点指针指向错误;递归终止条件写错。

示例测试:l1 = [1,2,4], l2 = [1,3,4],输出 [1,1,2,3,4,4]

138. 随机链表的复制(中等)

题目简述:给定一个带随机指针的复杂链表,每个节点包含 val、next、random 指针,深拷贝一个全新链表,新链表节点与原链表无任何指针关联,结构、数值、随机指针完全一致。

解题思路一:原地穿插复制+分离链表(无哈希最优解)

核心思路:原地复制节点穿插在原节点后方,统一赋值随机指针,最后分离新旧链表,全程无需哈希表,空间 O(1)。

解题步骤:1. 第一次遍历:每个原节点后插入复制节点,形成新旧交替链表;2. 第二次遍历:为所有复制节点赋值 random 指针;3. 第三次遍历:拆分原链表与复制链表,还原原链表、生成新链表。

复杂度:时间 O(n),空间 O(1)

优缺点:空间极致优化,面试高阶最优解法,无哈希冗余开销。

解题思路二:哈希表新旧映射(朴素易懂解)

核心思路:哈希表存储「原节点-新节点」映射,先复制所有节点数值,再统一赋值 next、random 指针,逻辑直白无难度。

复杂度:时间 O(n),空间 O(n)

优缺点:新手零bug解法,思维成本低;需要额外哈希空间存储映射关系。

易错点总结:随机指针赋值顺序错误;链表分离时节点断裂;未还原原链表结构。

示例测试:输入复杂链表,完成深拷贝,新链表所有指针与原链表一致且完全独立。

92. 反转链表 II(中等)

题目简述:给定链表头节点,反转区间 [left, right] 范围内的所有节点,仅反转局部区间,其余节点相对位置不变,返回修改后链表。

解题思路一:迭代局部反转+拼接(考场标准模板)

核心思路:找到反转区间前驱节点,对区间内节点进行局部迭代反转,最后将反转后的区间首尾与原链表前后节点拼接,保证链表不断裂。

解题步骤:1. 新建虚拟头节点,规避头部反转边界问题;2. 遍历找到区间前驱节点与区间首节点;3. 迭代反转区间内所有节点;4. 拼接前驱节点、反转区间、后续节点;5. 返回虚拟头节点下一节点。

复杂度:时间 O(n),空间 O(1)

优缺点:原地迭代、常数空间,边界处理完善,局部反转通用模板。

解题思路二:递归局部反转(进阶思路解)

核心思路:递归定位反转区间,逐层反转指定区间节点,回溯完成链表拼接。

复杂度:时间 O(n),空间 O(n)

优缺点:代码简洁;递归逻辑抽象、边界易错,不推荐考场手写。

易错点总结:区间首尾拼接错误导致链表成环/断裂;前驱节点定位失误;反转指针顺序错乱。

示例测试:head = [1,2,3,4,5], left = 2, right = 4,输出 [1,4,3,2,5]

25. K 个一组翻转链表(困难)

题目简述:给定链表头节点,每 k 个节点为一组进行反转,不足 k 个的末尾组保持原有顺序,返回翻转后的完整链表,属于链表高阶操作题。

解题思路一:迭代分组反转(最优稳定解)

核心思路:分组遍历链表,每次检查剩余节点是否够 k 个,够则迭代反转当前组,组间依次拼接,全程原地操作。

解题步骤:1. 新建虚拟头节点,记录每组前驱节点;2. 遍历统计剩余节点数量,判断是否可反转;3. 对合规分组进行局部反转;4. 拼接反转后的分组,更新下一组前驱节点;5. 不足k个直接保留,遍历结束返回结果。

复杂度:时间 O(n),空间 O(1)

优缺点:效率最高、无递归开销,边界严谨,面试高阶必背模板。

解题思路二:递归分组翻转(简洁进阶解)

核心思路:递归翻转前k个节点,剩余链表递归处理,将翻转后的组尾连接后续递归结果。

复杂度:时间 O(n),空间 O(n)

优缺点:代码简洁、逻辑清晰;递归栈消耗空间,超长链表不适用。

易错点总结:不足k个节点仍反转;组间拼接断裂;分组边界统计错误。

示例测试:head = [1,2,3,4,5], k = 2,输出 [2,1,4,3,5]

19. 删除链表的倒数第 N 个节点(中等)

题目简述:删除链表倒数第 n 个节点,返回链表头节点,进阶要求一趟遍历完成,不允许先统计链表长度。

解题思路一:快慢指针一趟遍历(最优模板解)

核心思路:快慢指针间隔 n 步,快指针先行 n 步,随后双指针同步后移,快指针到末尾时,慢指针恰好指向倒数第 n+1 节点,直接删除后继节点。

解题步骤:1. 新建虚拟头节点,规避删除头节点边界问题;2. 快慢指针指向虚拟头节点;3. 快指针先行 n+1 步;4. 快慢指针同步后移,直至快指针为空;5. 删除慢指针后继节点,返回结果链表。

复杂度:时间 O(n),空间 O(1)

优缺点:一趟遍历、常数空间,完美满足进阶要求,考场首选。

解题思路二:两次遍历(朴素易懂解)

核心思路:第一次遍历统计链表总长度,第二次遍历定位倒数第n个节点的前驱节点,完成删除。

复杂度:时间 O(n),空间 O(1)

优缺点:逻辑简单、零思维难度;需要两次遍历,不满足最优进阶要求。

易错点总结:快慢指针间隔步数错误;未使用虚拟头节点,删除头节点报错;删除节点后未断开指针。

示例测试:head = [1,2,3,4,5], n = 2,输出 [1,2,3,5]

82. 删除排序链表重复元素 II(中等)

题目简述:给定升序排序链表,删除所有存在重复的节点,重复节点全部删除,不保留任何重复值,仅保留出现一次的唯一节点。

解题思路一:虚拟头节点+迭代遍历(标准去重模板)

核心思路:利用链表有序特性,重复节点必然连续,虚拟头节点处理头部重复边界,遍历过程中批量跳过所有重复节点,只保留唯一节点。

解题步骤:1. 新建虚拟头节点,指向链表头部;2. 定义遍历指针,遍历链表;3. 发现当前节点与下一节点值重复,记录重复值;4. 循环跳过所有等于重复值的节点;5. 无重复则指针后移,持续遍历至末尾。

复杂度:时间 O(n),空间 O(1)

优缺点:一趟遍历、原地去重,全覆盖头部、尾部、连续重复场景,无边界漏洞。

解题思路二:递归去重(简洁进阶解)

核心思路:递归判断当前节点是否重复,重复则直接返回后续递归结果,不重复则保留当前节点拼接后续链表。

复杂度:时间 O(n),空间 O(n)

优缺点:代码极简;递归栈开销,超长链表不适用。

易错点总结:只删除单个重复节点,未全量删除;头部重复处理失败;指针跳转错误导致漏删。

示例测试:head = [1,2,3,3,4,4,5],输出 [1,2,5]

61. 旋转链表(中等)

题目简述:给定链表头节点,将链表每个节点向右循环旋转 k 位,即尾部 k 个节点移动至链表头部,保持节点原有相对顺序。

解题思路一:成环断环法(极简最优解)

核心思路:闭环操作经典技巧,将链表首尾相连成环,计算有效旋转步数,找到新链表尾节点,断开环得到新链表,全程极简高效。

解题步骤:1. 特判空链表或单节点链表,直接返回;2. 遍历统计链表长度,同时将链表首尾相连成环;3. 计算有效旋转步数 k = k % len,规避无效旋转;4. 找到新链表尾节点位置,断开环;5. 返回新头节点。

复杂度:时间 O(n),空间 O(1)

优缺点:代码极简、逻辑优雅,是本题最优专属模板,无冗余遍历。

解题思路二:多次尾部移动(朴素低效解)

核心思路:模拟旋转过程,循环 k 次,每次将末尾节点移至头部。

复杂度:时间 O(kn),空间 O(1)

优缺点:思路直白;k值较大时效率极低,大数据超时。

易错点总结:未取模导致无效旋转;断环位置错误;成环后未正确断开导致循环链表。

示例测试:head = [1,2,3,4,5], k = 2,输出 [4,5,1,2,3]

86. 分隔链表(中等)

题目简述:给定链表与阈值 x,将链表分隔为两部分,小于 x 的节点在前,大于等于 x 的节点在后,保持两部分内部节点原有相对顺序。

解题思路一:双虚拟头节点拼接(标准最优解)

核心思路:创建大小两个虚拟头节点,遍历原链表,将小值节点、大值节点分别归入两个链表,最后拼接大小链表,完美保持相对顺序。

解题步骤:1. 创建小值链表、大值链表虚拟头节点;2. 遍历原链表所有节点;3. 节点值小于x接入小链表,否则接入大链表;4. 遍历结束,将小链表尾部拼接大链表头部;5. 大链表尾部置空,防止成环,返回小链表头节点。

复杂度:时间 O(n),空间 O(1)

优缺点:一趟遍历、原地分隔、顺序无错乱,逻辑零漏洞,考场必背。

解题思路二:单次遍历原地调整(进阶精简解)

核心思路:不创建多余虚拟节点,直接遍历原链表,通过指针原地调整节点位置,将小于阈值的节点前置,大于等于阈值的节点后置,节省少量变量开销。

复杂度:时间 O(n),空间 O(1)

优缺点:极致精简变量;指针跳转逻辑复杂,边界极易出错,不推荐考场使用。

易错点总结:未将大链表尾部置空,导致链表出现环;节点拼接顺序错误,打乱原有相对顺序;头尾节点边界处理遗漏。

示例测试:head = [1,4,3,2,5,2], x = 3,输出 [1,2,2,4,3,5]

146. LRU 缓存(中等)

题目简述:设计并实现 LRU(最近最少使用)缓存机制,支持 getput 操作。get(key):key存在返回对应值,不存在返回-1;put(key,value):key存在则更新值,不存在则插入键值对。当缓存容量达到上限时,需删除最近最少使用的数据。要求所有操作时间复杂度 O(1)。

解题思路一:哈希表+双向链表(标准最优解、面试必考模板)

核心思路:结合哈希表快速查找、双向链表快速增删的优势,双向链表按使用时序排序,表头为最近使用节点,表尾为最少使用节点,哈希表存储key与链表节点的映射关系,实现O(1)增删改查。

解题步骤:1. 自定义双向链表节点,存储key、value、前驱、后继指针;2. 初始化虚拟头尾节点,构建空双向链表,初始化哈希表、缓存容量、当前计数;3. get操作:key不存在返回-1;存在则取出节点,移除原位置,插入链表头部(标记为最近使用),返回节点值;4. put操作:key存在则更新节点值,并移至表头;key不存在则新建节点,插入表头,哈希表新增映射;5. 插入后若超出容量,删除链表尾部最少使用节点,同时清除哈希表对应映射。

复杂度:get/put操作均为 O(1),空间 O(capacity)

优缺点:完全满足题目时间复杂度要求,是工程标准LRU实现方案,面试核心模板;需要自定义双向链表,代码量偏大。

解题思路二:语言有序哈希容器(极简偷懒解)

核心思路:利用编程语言内置的有序哈希结构(Java LinkedHashMap、Python OrderedDict),自带访问排序、自动移除最久未使用元素特性,几行代码实现LRU缓存。

复杂度:所有操作 O(1)

优缺点:代码极简、零手写数据结构;面试不允许使用内置工具类,仅适合刷题快速AC。

易错点总结:更新/访问节点后未移动至表头,时序排序错乱;缓存溢出时未同步删除哈希表映射,导致内存泄漏;双向链表头尾指针拼接错误导致断链或成环;新增节点未计数、删除节点未更新容量。

示例测试:依次执行 put(1,1),put(2,2),get(1),put(3,3),get(2),put(4,4),get(1),get(3),get(4),输出 [1,-1,3,4]

核心技巧复盘:LRU核心精髓为哈希表查节点,双向链表管时序,所有使用过的节点置顶,容量超限淘汰尾部节点,是设计类题型高频标杆题。

九、二叉树专题

二叉树为核心非线性数据结构专题,核心依托DFS深度优先递归BFS广度层序遍历两大模板,结合分治回溯、动态规划、哈希预处理等思想,可高效解决树的遍历、构造、翻转对称、路径统计、公共祖先、层级运算等全品类高频树形题型,是算法面试必考核心模块。

104. 二叉树的最大深度(简单)

题目简述:给定二叉树根节点,返回二叉树的最大深度,即根节点到最远叶子节点的最长路径上的节点数。

解题思路一:DFS递归分治(极简最优模板)

核心思路:分治思想,当前节点深度 = 1 + 左右子树最大深度,递归直达叶子节点,回溯逐层更新最大深度。

解题步骤:1. 递归终止条件:节点为空,返回深度0;2. 递归求解左子树最大深度、右子树最大深度;3. 当前节点深度为 1 + 左右子树深度的最大值;4. 逐层回溯,最终返回根节点对应最大深度。

复杂度:时间 O(n),遍历所有节点;空间 O(h),h为树高,递归栈开销

优缺点:代码极简、逻辑清晰,二叉树DFS入门必背模板;树极度倾斜时递归栈深度过大。

解题思路二:BFS层序遍历(迭代稳妥解)

核心思路:队列实现层序遍历,每遍历完整一层,深度+1,最终层数即为最大深度。

复杂度:时间 O(n),空间 O(n)

优缺点:无递归栈溢出风险,适合极端倾斜二叉树;代码相比递归稍繁琐。

易错点总结:空节点深度判断错误;叶子节点递归终止条件混淆。

示例测试:root = [3,9,20,null,null,15,7],输出 3

100. 相同的树(简单)

题目简述:给定两棵二叉树根节点,判断两棵树是否完全相同,要求节点值相同、树结构完全一致

解题思路一:双节点递归校验(标准模板)

核心思路:同步递归遍历两棵树,双重校验:当前节点值相等 + 左子树完全相同 + 右子树完全相同。

解题步骤:1. 两节点均为空,结构一致,返回 true;2. 一个为空、一个不为空,结构不同,返回 false;3. 两节点值不相等,返回 false;4. 递归校验左左子树、右右子树,全部满足则返回 true。

复杂度:时间 O(min(n,m)),遍历至首个不同节点终止;空间 O(min(h1,h2))

优缺点:逻辑严谨、边界全覆盖,结构+数值双重校验,无冗余遍历。

解题思路二:层序迭代同步遍历(非递归解)

核心思路:双队列同步存储两棵树节点,逐层对比节点值与结构,出现不一致直接判定不相同。

复杂度:时间 O(min(n,m)),空间 O(n)

优缺点:规避递归风险;代码量大,适合禁止递归场景。

易错点总结:仅校验节点值,忽略树结构校验;空节点判断顺序错误。

示例测试:p = [1,2,3], q = [1,2,3] 输出 true;p = [1,2], q = [1,null,2] 输出 false

226. 翻转二叉树(简单)

题目简述:给定二叉树根节点,翻转二叉树,将每个节点的左右子树互换,返回翻转后的树根节点。

解题思路一:后序递归翻转(经典极简解)

核心思路:自底向上翻转,先递归翻转左右子树,再交换当前节点的左右指针,标准二叉树递归模板。

解题步骤:1. 节点为空直接返回;2. 递归翻转左子树、右子树;3. 交换当前节点的左、右子节点指针;4. 返回当前节点。

复杂度:时间 O(n),遍历所有节点;空间 O(h)

优缺点:代码极简、逻辑直观,二叉树最经典题型,面试高频手撕题。

解题思路二:层序迭代翻转(BFS非递归解)

核心思路:队列层序遍历所有节点,遍历过程中逐个交换每个节点的左右子节点,自上而下完成翻转。

复杂度:时间 O(n),空间 O(n)

优缺点:无递归栈溢出问题,迭代逻辑稳妥。

易错点总结:先交换指针再递归翻转,逻辑颠倒;空节点未判空导致报错。

示例测试:root = [4,2,7,1,3,6,9],翻转后输出 [4,7,2,9,6,3,1]

101. 对称二叉树(简单)

题目简述:给定二叉树根节点,判断二叉树是否为轴对称二叉树,即左右子树呈镜像对称。

解题思路一:双节点镜像递归(最优模板)

核心思路:将根节点左右子树视为两棵树,递归校验:左树左节点 = 右树右节点、左树右节点 = 右树左节点,实现镜像校验。

解题步骤:1. 根节点为空,直接返回 true;2. 定义递归函数,接收两个镜像节点;3. 两节点均空,对称;一空一非空,不对称;值不同,不对称;4. 递归校验左左vs右右、左右vs右左,双向镜像匹配。

复杂度:时间 O(n),空间 O(h)

优缺点:双节点递归思维通用,适配所有镜像对称题型,代码简洁高效。

解题思路二:队列迭代镜像校验(非递归解)

核心思路:队列成对存入镜像节点,每次取出一对节点对比,再反向存入子节点,逐层校验对称性。

复杂度:时间 O(n),空间 O(n)

优缺点:迭代稳定无栈溢出,适合大数据树。

易错点总结:子节点匹配顺序错误;忽略空节点对称场景。

示例测试:root = [1,2,2,3,4,4,3] 输出 true;root = [1,2,2,null,3,null,3] 输出 false

105. 从前序与中序遍历序列构造二叉树(中等)

题目简述:根据二叉树的前序遍历、中序遍历数组,还原构建完整二叉树,无重复节点值。

解题思路一:递归分治+哈希表优化(考场最优解)

核心思路:前序首元素为根节点,通过哈希表O(1)查找根节点在中序的位置,划分中序左右区间,递归构造左右子树。

解题步骤:1. 哈希表预处理中序数组,存储数值-下标映射;2. 前序遍历首个元素为当前根节点;3. 通过根节点下标,拆分中序数组为左子树区间、右子树区间;4. 根据区间长度,对应截取前序数组左右子树区间;5. 递归构造左、右子树,拼接至根节点。

复杂度:时间 O(n),哈希表查询优化;空间 O(n)

优缺点:规避每次遍历查找根节点的O(n)开销,效率最优,构造树通用模板。

解题思路二:暴力递归(朴素易懂解)

核心思路:每次遍历中序数组查找根节点位置,划分区间递归构造,无需哈希表。

复杂度:时间 O(n²),空间 O(n)

优缺点:无需预处理哈希表,逻辑直白;大数据量耗时严重。

易错点总结:前后区间长度不匹配;左右子树边界截取错误;根节点定位偏差。

示例测试:preorder = [3,9,20,15,7], inorder = [9,3,15,20,7],还原对应二叉树

106. 从中序与后序遍历序列构造二叉树(中等)

题目简述:根据二叉树的后序遍历、中序遍历数组,还原构建完整二叉树,无重复节点值。

解题思路一:递归分治+哈希表(同105通用模板)

核心思路:复用105题构造模板,后序遍历末尾元素为根节点,哈希表定位中序根节点位置,划分左右区间递归构造。

解题步骤:1. 哈希表存储中序数值下标映射;2. 后序末尾元素为当前根节点;3. 拆分中序左右子树区间,根据区间长度截取后序左右区间;4. 先递归构造右子树、再构造左子树,拼接根节点。

复杂度:时间 O(n),空间 O(n)

优缺点:与105题逻辑高度统一,一套模板通吃前中、中后构造二叉树。

易错点总结:后序区间截取顺序错误;左右子树递归顺序颠倒。

示例测试:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3],还原对应二叉树

117. 填充每个节点的下一个右侧节点指针 II(中等)

题目简述:给定任意二叉树,为每个节点新增next指针,指向同层右侧相邻节点,无右侧节点则指向null。

解题思路一:BFS层序遍历(通用万能解)

核心思路:队列逐层遍历节点,每层内逐个节点依次绑定next指针,最后一个节点指向null,适配任意非完美二叉树。

解题步骤:1. 初始化队列,存入根节点;2. 循环遍历每层节点,记录当前层节点数量;3. 遍历当前层所有节点,除最后一个节点外,其余节点next指向右侧相邻节点;4. 依次入队左右子节点,直至队列为空。

复杂度:时间 O(n),空间 O(n)

优缺点:逻辑简单、适配所有二叉树,无边界漏洞,层序经典变种题。

解题思路二:迭代链表遍历(空间优化解)

核心思路:利用上一层已建立的next指针遍历当前层,无需队列,常数空间完成链接。

复杂度:时间 O(n),空间 O(1)

优缺点:空间极致优化;指针逻辑复杂,面试按需选择。

易错点总结:跨层错误链接;每层末尾节点未置空next。

114. 二叉树展开为链表(中等)

题目简述:将二叉树原地展开为单链表,链表顺序为二叉树前序遍历顺序,所有节点右指针为后继节点,左指针置空。

解题思路一:后序递归原地拼接(最优原地解)

核心思路:自底向上处理,先展开左右子树,将右子树拼接至左子树末尾,再将左子树转为右子树,左指针置空。

解题步骤:1. 节点为空直接返回;2. 递归展开左、右子树;3. 保存原右子树;4. 将左子树赋值给节点右指针,左指针置空;5. 找到当前右子树末尾节点,拼接原右子树。

复杂度:时间 O(n),空间 O(h)

优缺点:完全原地修改,无额外空间存储节点,符合题目进阶要求。

解题思路二:前序遍历缓存拼接(朴素解)

核心思路:前序遍历收集所有节点,再依次遍历节点绑定右指针、清空左指针。

复杂度:时间 O(n),空间 O(n)

优缺点:逻辑简单零出错;占用额外数组空间,非原地算法。

易错点总结:未清空左指针;子树拼接顺序错乱,破坏前序顺序。

示例测试:root = [1,2,5,3,4,null,6],展开后输出 [1,null,2,null,3,null,4,null,5,null,6]

112. 路径总和(简单)

题目简述:判断二叉树是否存在根到叶子的路径,使路径上所有节点值之和等于目标目标和 targetSum。

解题思路一:DFS递归递减(简洁模板)

核心思路:自顶向下递减目标值,到达叶子节点时判断剩余数值是否为0,存在合法路径即返回true。

解题步骤:1. 节点为空返回false;2. 目标值减去当前节点值;3. 当前节点为叶子节点,判断剩余值是否为0;4. 非叶子节点,递归判断左子树或右子树是否存在合法路径。

复杂度:时间 O(n),空间 O(h)

优缺点:短路求值,找到路径立即返回,效率高,路径类基础模板。

解题思路二:BFS层序累加(迭代解)

核心思路:队列同步存储节点与当前路径和,逐层累加,叶子节点匹配目标和即存在路径。

复杂度:时间 O(n),空间 O(n)

优缺点:迭代稳定,无递归开销。

易错点总结:非叶子节点判定返回;路径和计算累加逻辑错误。

示例测试:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22,输出 true

129. 求根节点到叶节点数字之和(中等)

题目简述:二叉树每个节点存放0-9数字,每条根到叶子路径代表一个整数,求所有路径数字的累加总和。

解题思路一:DFS路径拼接求和(标准模板)

核心思路:自顶向下累计数值,每向下一层,当前数值 *10 + 子节点值,叶子节点汇总数值至总和。

解题步骤:1. 定义递归函数,传入当前节点与累计数值;2. 节点为空直接返回;3. 更新当前累计数值 = pre*10 + val;4. 叶子节点则累加至全局总和;5. 递归遍历左右子树。

复杂度:时间 O(n),空间 O(h)

优缺点:一趟遍历完成累加,逻辑贴合题目路径数字拼接规则。

解题思路二:BFS层序迭代求和(非递归解)

核心思路:队列存储节点与当前路径数值,逐层更新数值,叶子节点累加结果。

复杂度:时间 O(n),空间 O(n)

优缺点:规避递归,适合深度极大的二叉树。

易错点总结:数值进位计算错误;非叶子节点汇总结果。

示例测试:root = [1,2,3],路径12+13=25,输出 25

124. 二叉树中的最大路径和(困难)

题目简述:给定二叉树,计算任意路径的最大和,路径可经过任意节点、无需经过根节点、无需根到叶,路径只能左右单向延伸。

解题思路一:后序DFS+全局最值(二叉树DP难题模板)

核心思路:自底向上递归,返回当前节点单侧最大贡献值(只能选左、右或自身),同时更新「当前节点+左链+右链」的全局最大路径和。

解题步骤:1. 初始化全局最大值为极小值;2. 递归函数返回当前节点可向上贡献的最大和;3. 左、右子树贡献值小于0则舍弃(取0);4. 更新全局最大路径和 = 节点值+左贡献+右贡献;5. 向上返回 节点值+max(左贡献,右贡献)。

复杂度:时间 O(n),空间 O(h)

优缺点:唯一最优解法,二叉树动态规划标杆题,兼顾局部贡献与全局最值。

易错点总结:未舍弃负数子路径;向上返回值与全局最大值混淆;全负数树边界处理错误。

示例测试:root = [-10,9,20,null,null,15,7],最大路径15+20+7=42,输出 42

173. 二叉搜索树迭代器(中等)

题目简述:实现二叉搜索树BST的迭代器,包含next()、hasNext()方法,按中序升序遍历节点,要求平均O(1)时间、O(h)空间。

解题思路一:栈受控递归(标准面试解法)

核心思路:模拟中序遍历迭代过程,栈缓存左链节点,惰性加载节点,实现受控递归,满足复杂度要求。

解题步骤:1. 初始化栈,将根节点所有左节点入栈;2. next():弹出栈顶节点,遍历其右子树所有左节点入栈,返回当前节点值;3. hasNext():判断栈是否非空。

复杂度:next平均O(1),单次最坏O(h);空间 O(h)

优缺点:完全符合题目复杂度限制,工程标准实现,面试高频设计题。

解题思路二:预处理全遍历(朴素偷懒解)

核心思路:初始化一次性中序遍历存储所有节点值,后续直接数组遍历取值。

复杂度:初始化O(n),next O(1);空间 O(n)

优缺点:代码简单;空间不达标,不满足进阶要求。

易错点总结:右子树左节点未全部入栈;迭代顺序错乱破坏BST升序规则。

222. 完全二叉树的节点个数(简单)

题目简述:统计完全二叉树的节点总数,进阶要求利用完全二叉树性质优化时间复杂度,优于普通O(n)遍历。

解题思路一:性质优化递归(最优进阶解)

核心思路:完全二叉树特性:满二叉树节点数=2^h-1。判断左右子树高度是否相等,相等则左子树为满二叉树,直接公式计算,不等则递归统计。

解题步骤:1. 节点为空返回0;2. 统计最左深度、最右深度;3. 左右深度相等,当前树为满二叉树,返回 2^h -1;4. 不等则递归统计左、右子树节点数,求和+1。

复杂度:时间 O(log²n),远优于普通遍历;空间 O(logn)

优缺点:充分利用完全二叉树性质,时间复杂度最优,面试进阶考点。

解题思路二:普通DFS/BFS遍历(朴素解)

核心思路:常规遍历所有节点,计数统计总数。

复杂度:时间 O(n),空间 O(h)

优缺点:简单直观;未利用题目特性,复杂度未优化。

易错点总结:左右深度统计错误;满二叉树公式记错。

236. 二叉树的最近公共祖先(中等)

题目简述:给定二叉树根节点与两个节点p、q,返回两节点的最近公共祖先LCA,最近公共祖先定义为深度最大、同时包含两节点的祖先节点。

解题思路一:后序递归LCA模板(万能最优解)

核心思路:后序自底向上查找,左右子树各找到一个节点,当前节点即为最近公共祖先;单侧找到则向上传递结果。

解题步骤:1. 节点为空、等于p、等于q,直接返回当前节点;2. 递归获取左、右子树查找结果;3. 左右结果均不为空,当前节点为LCA,返回自身;4. 单侧不为空返回单侧结果,双侧为空返回空。

复杂度:时间 O(n),空间 O(h)

优缺点:二叉树LCA通用模板,无需预处理,一趟遍历求解,面试必背。

解题思路二:哈希表存储父节点(迭代解)

核心思路:遍历二叉树,哈希表记录所有节点父节点,回溯p、q的祖先链,找首个公共节点。

复杂度:时间 O(n),空间 O(n)

优缺点:迭代逻辑易懂;需要额外哈希空间。

易错点总结:递归返回条件缺失;公共祖先传递逻辑错乱。

102. 二叉树的层序遍历(中等)

题目简述:按层遍历二叉树,返回每层节点值组成的二维数组,从上至下、从左至右。

解题思路一:BFS队列层序(通用模板)

核心思路:队列存储当前层节点,通过每层长度控制遍历范围,逐层收集结果,层序遍历万能模板。

解题步骤:1. 空树直接返回空数组;2. 初始化队列存入根节点;3. 循环获取当前层节点数量,遍历当前层所有节点;4. 收集节点值,入队左右子节点;5. 每层遍历完成存入结果集。

复杂度:时间 O(n),空间 O(n)

优缺点:所有层序变种题的基础模板,代码固定、零边界漏洞。

解题思路二:DFS递归分层(进阶思路解)

核心思路:递归时记录当前节点层数,将节点值存入对应层级数组。

复杂度:时间 O(n),空间 O(h)

优缺点:空间更优;逻辑不如BFS直观,适合拓展思路。

示例测试:root = [3,9,20,null,null,15,7],输出 [[3],[9,20],[15,7]]

199. 二叉树的右视图(中等)

题目简述:返回二叉树每层最右侧节点组成的数组,即从右侧观察二叉树看到的节点序列。

解题思路一:BFS层序取末尾(最简变种解)

核心思路:复用层序遍历模板,每层遍历结束后,取当前层最后一个节点值存入结果。

复杂度:时间 O(n),空间 O(n)

优缺点:基于基础层序模板改造,改动极小,不易出错。

解题思路二:DFS逆序递归(空间优化解)

核心思路:优先递归遍历右子树,首次遍历到当前层级即为最右侧节点,直接记录结果。

复杂度:时间 O(n),空间 O(h)

优缺点:空间更优,思路巧妙。

示例测试:root = [1,2,3,null,5,null,4],输出 [1,3,4]

637. 二叉树的层平均值(简单)

题目简述:计算二叉树每一层节点值的平均值,返回所有层平均值组成的数组。

解题思路一:BFS层序求和平均(基础模板)

核心思路:层序遍历每层节点,统计当前层总和与节点个数,遍历完成计算平均值。

复杂度:时间 O(n),空间 O(n)

优缺点:层序基础应用题,逻辑简单、无难点。

解题思路二:DFS分层统计(进阶解)

核心思路:递归记录层级,分别维护每层总和、节点数量数组,最后统一计算平均值。

复杂度:时间 O(n),空间 O(h)

示例测试:root = [3,9,20,null,null,15,7],输出 [3.00000,14.50000,11.00000]

103. 二叉树的锯齿形层序遍历(中等)

题目简述:二叉树层序遍历,奇数层从左到右、偶数层从右到左,形成锯齿形遍历结果。

解题思路一:BFS层序+奇偶反转(标准变种解)

核心思路:常规层序遍历收集每层节点,通过标记位判断层级奇偶,偶数层反转当前层结果。

解题步骤:1. 基础层序遍历收集每层数组;2. 初始化标记位,首层不反转;3. 偶数层反转数组后存入结果;4. 切换标记位,循环遍历。

复杂度:时间 O(n),空间 O(n)

优缺点:基于基础模板改造,改动简单、不易出错,锯齿遍历通用解法。

解题思路二:双端队列取值(优化解)

核心思路:利用双端队列特性,奇数层队首取值,偶数层队尾取值,无需反转数组。

复杂度:时间 O(n),空间 O(n)

优缺点:省去反转操作,效率小幅提升,代码稍复杂。

示例测试:root = [3,9,20,null,null,15,7],输出 [[3],[20,9],[15,7]]

十、二叉搜索树专题(BST)

BST核心解题总纲:二叉搜索树核心性质——中序遍历结果严格升序;任意节点,左子树所有节点值 < 当前节点值 < 右子树所有节点值。绝大多数BST题目均可通过「中序遍历」或「上下界递归校验」解决。

530. 二叉搜索树的最小绝对差(简单)

题目简述:给定一棵二叉搜索树,计算树中任意两个不同节点值的最小绝对差值。

解题思路一:中序遍历迭代+相邻比较(最优模板)

核心思路:利用BST中序遍历有序特性,遍历得到升序序列,最小差值必然出现在相邻两个节点之间,遍历过程实时更新最小值。

解题步骤:1. 栈实现中序迭代遍历,规避递归全局变量问题;2. 记录上一个遍历的节点值;3. 每遍历一个新节点,计算与前一节点的差值;4. 不断更新最小绝对差,遍历完成得到结果。

复杂度:时间 O(n),遍历所有节点一次;空间 O(h),栈存储树高节点

优缺点:一趟遍历完成求解,空间最优,充分利用BST有序特性,无冗余计算。

解题思路二:递归中序遍历(朴素简洁解)

核心思路:递归实现中序遍历,维护全局前驱节点值与最小差值,遍历相邻比较更新。

复杂度:时间 O(n);空间 O(h),递归栈开销

优缺点:代码极简易懂;依赖全局变量,工程性稍差。

易错点总结:忽略BST有序特性,暴力双重遍历超时;首个节点无前驱,误参与差值计算。

示例测试:root = [4,2,6,1,3],相邻差值为1、1、1、3,输出最小绝对差 1

230. 二叉搜索树中第K小的元素(中等)

题目简述:给定二叉搜索树与整数k,返回树中第k个最小的元素。

解题思路一:中序迭代+提前终止(最优考场解)

核心思路:BST中序遍历升序,迭代遍历过程中计数,遍历到第k个节点直接返回值,无需遍历整树,节省时间。

解题步骤:1. 栈迭代实现中序遍历;2. 每弹出一个节点,计数+1;3. 当计数等于k时,直接返回当前节点值,终止遍历。

复杂度:平均 O(h+k),最坏 O(n);空间 O(h)

优缺点:支持提前剪枝,无需遍历全部节点,考场效率最高,BST经典模板。

解题思路二:递归遍历存储+取值(朴素解)

核心思路:递归中序遍历收集所有节点值为升序数组,直接取下标k-1元素。

复杂度:时间 O(n),空间 O(n)

优缺点:逻辑零难度;必须遍历整树、占用数组空间,数据量大效率低。

易错点总结:计数起始值错误,下标错位;未提前终止遍历,冗余耗时。

示例测试:root = [3,1,4,null,2], k = 3,升序序列[1,2,3,4],输出 3

98. 验证二叉搜索树(中等)

题目简述:验证一棵树是否为合法二叉搜索树,合法要求:左子树所有节点值<根值,右子树所有节点值>根值,左右子树均为BST。

解题思路一:上下界递归校验(标准最优模板)

核心思路:为每个节点绑定取值上下边界,左子树更新上界为当前节点值,右子树更新下界为当前节点值,全程严格区间校验,规避仅比较父子节点的错误。

解题步骤:1. 根节点初始上下界为无穷小、无穷大;2. 当前节点值超出区间范围,直接返回false;3. 递归校验左子树:上界更新为当前节点值,下界不变;4. 递归校验右子树:下界更新为当前节点值,上界不变;5. 所有节点校验通过则返回true。

复杂度:时间 O(n),遍历所有节点;空间 O(h)

优缺点:全覆盖所有边界场景,解决跨层节点校验漏洞,面试标准解法。

解题思路二:中序遍历有序校验(稳妥迭代解)

核心思路:中序遍历BST,校验遍历序列是否严格单调递增,无序则不是合法BST。

复杂度:时间 O(n),空间 O(h)

优缺点:逻辑直观,无区间边界溢出问题;无法提前剪枝,必须遍历完整树。

易错点总结:仅比较当前节点与左右子节点,忽略跨层大小限制;未严格区分大于/大于等于、小于/小于等于。

示例测试:root = [2,1,3] 输出 true;root = [5,1,4,null,null,3,6] 输出 false

十一、图专题

图算法核心解题总纲:1. 网格图:DFS/BFS遍历、沉岛、边界预处理;2. 无权图:BFS求最短路径、双向BFS优化;3. 带权图:建图+搜索求解路径权重;4. 有向图:拓扑排序判环、输出序列,核心依赖入度表+队列。

200. 岛屿数量(中等)

题目简述:给定由 '1'(陆地)和 '0'(水域)组成的二维网格,统计网格中岛屿数量,岛屿由相邻陆地上下左右相连形成。

解题思路一:DFS沉岛法(网格通用模板)

核心思路:遍历网格,遇到陆地则计数+1,随即DFS递归将相连所有陆地改为水域(沉岛),避免重复统计。

解题步骤:1. 双层循环遍历网格每个单元格;2. 遇到未访问陆地,岛屿数+1;3. DFS向上下左右四个方向扩散,将所有相连陆地置0;4. 遍历完成后返回总岛屿数。

复杂度:时间 O(mn),m、n为网格行列数;空间 O(mn),最坏全陆地递归栈开销

优缺点:网格遍历经典模板,逻辑简洁、代码固定,适配绝大多数网格题型。

解题思路二:BFS广度遍历(迭代防栈溢出)

核心思路:遍历到陆地后,队列迭代扩散遍历所有相连陆地,逐一置0沉岛,统计岛屿数量。

复杂度:时间 O(m*n);空间 O(min(m,n))

优缺点:无递归栈溢出风险,适合超大网格数据。

易错点总结:越界判断缺失导致报错;重复统计同一岛屿;四个方向遍历不全。

示例测试:grid = [["1","1","0","0","0"],["1","1","0","0","0"],["0","0","1","0","0"],["0","0","0","1","1"]],输出 3

130. 被围绕的区域(中等)

题目简述:给定二维网格,将所有被完全包围的 'O' 替换为 'X',边界上的 'O' 及与其相连的 'O' 不被替换。

解题思路一:反向思维+边界预处理(最优解)

核心思路:正向判断包围复杂,反向处理:先标记所有边界连通O为临时字符,剩余内部O即为被包围区域,最后统一替换字符。

解题步骤:1. 遍历网格四条边界,将所有边界连通O标记为#;2. 遍历整个网格,剩余O全部替换为X;3. 将临时标记#还原为O。

复杂度:时间 O(mn),遍历网格三次;空间 O(mn)最坏情况

优缺点:反向思维大幅简化逻辑,无复杂包围判断,本题唯一高效解法。

易错点总结:仅遍历四角,遗漏边界中间位置O;替换顺序错乱,导致全部替换错误。

示例测试:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]],处理后内部O被替换,输出对应网格

133. 克隆图(中等)

题目简述:给定无向连通图,返回该图的深拷贝,新图所有节点、邻接关系与原图完全一致,不得返回原节点。

解题思路一:DFS+哈希表去重(图复制模板)

核心思路:哈希表存储「原节点-克隆节点」映射,避免重复创建节点;DFS遍历原图,递归复制邻接关系。

解题步骤:1. 哈希表记录已克隆节点,防止重复创建;2. 当前节点已克隆,直接返回缓存节点;3. 未克隆则新建节点,存入哈希表;4. 递归遍历原节点所有邻接点,绑定克隆节点邻接关系。

复杂度:时间 O(n),n为节点+边总数;空间 O(n)

优缺点:图深拷贝通用模板,完美处理环图、重复邻接节点问题。

解题思路二:BFS迭代克隆(非递归解)

核心思路:队列遍历原图节点,哈希表缓存克隆节点,逐层复制节点与邻接关系。

复杂度:时间 O(n),空间 O(n)

优缺点:迭代稳定无递归风险,逻辑清晰。

易错点总结:未缓存已克隆节点,导致重复创建、死循环;邻接关系绑定顺序错误。

399. 除法求值(中等)

题目简述:给定变量除法算式与结果,根据已知条件求解多组查询除法结果,无法求解返回-1.0。

解题思路一:带权图+DFS遍历(通用解法)

核心思路:将变量视为节点,除法关系视为带权有向边,a/b=2 即a→b权值2、b→a权值0.5,查询即为求解两点路径权值乘积。

解题步骤:1. 构建哈希邻接表,存储节点与带权邻接点;2. 针对每组查询,DFS搜索两点间路径;3. 累加路径权值乘积,无路径返回-1。

复杂度:建图O(n),单次查询O(n);空间 O(n)

优缺点:直观理解带权图运算,适配所有除法查询场景。

解题思路二:并查集带权(最优高效解)

核心思路:带权并查集维护节点连通关系与相对权值,查询时判断是否连通,连通则计算权值比值。

复杂度:建图与查询均近似 O(1),效率极高

优缺点:适合大量查询场景;带权并查集逻辑较抽象。

易错点总结:反向边权值计算错误;未判断节点是否存在、是否连通。

207. 课程表(中等)

题目简述:给定课程总数与先修课程关系,判断是否能完成所有课程学习,本质判断有向图是否存在环。

解题思路一:拓扑排序+BFS判环(标准模板)

核心思路:构建邻接表与入度数组,不断入队入度为0的节点,删除该节点出边,最终统计遍历节点数,小于总课程数则有环。

解题步骤:1. 初始化邻接表、入度数组;2. 遍历先修关系,构建有向边、更新入度;3. 入队所有入度为0的节点;4. 出队节点计数+1,遍历其邻接点,入度-1,入度为0继续入队;5. 最终计数==总课程数,无环可完成,反之有环。

复杂度:时间 O(n+m),n课程数、m先修关系数;空间 O(n+m)

优缺点:有向图判环万能模板,代码固定,面试高频。

易错点总结:入度更新错误;邻接表建边方向颠倒。

210. 课程表 II(中等)

题目简述:给定课程与先修关系,返回任意一种合法课程学习顺序,无法完成则返回空数组。

解题思路一:拓扑排序+BFS输出序列(进阶模板)

核心思路:完全复用207拓扑排序逻辑,遍历过程中记录出队节点,最终出队序列即为合法学习顺序。

解题步骤:1. 构建邻接表、入度数组;2. 入队入度为0节点;3. 出队节点加入结果序列,更新邻接点入度;4. 遍历完成,序列长度达标返回结果,否则返回空数组。

复杂度:时间 O(n+m),空间 O(n+m)

优缺点:拓扑排序输出序列标准模板,与判环题一脉相承。

易错点总结:结果序列收集时机错误;有环场景未做空数组处理。

909. 蛇梯棋(中等)

题目简述:N*N蛇梯棋盘,规则:每次走1-6步,遇梯子直达顶端、遇蛇直达尾部,求从起点到终点的最少移动次数。

解题思路一:BFS最短路径(无权图最优解)

核心思路:棋盘每个格子为节点,每次投掷骰子为6条无向边,BFS天然求解无权图最短路径,遇到蛇/梯子直接跳转节点。

解题步骤:1. 一维映射二维棋盘坐标,适配蛇梯跳转规则;2. 队列存储当前位置与步数,标记已访问节点;3. 每次遍历6种走法,跳转蛇梯目标位置;4. 到达终点直接返回步数,遍历无结果返回-1。

复杂度:时间 O(n²),n为棋盘边长;空间 O(n²)

优缺点:无权图最短路径标准BFS思路,逻辑直白。

易错点总结:棋盘奇偶行翻转坐标映射错误;重复访问节点导致死循环。

433. 最小基因变化(中等)

题目简述:给定起始基因、目标基因、基因库,每次可替换一个字符,求从起始到目标的最少变化次数,无法完成返回-1。

解题思路一:字符串BFS(最短路径模板)

核心思路:每个基因为节点,单次字符变化为边,BFS逐层遍历,首次到达目标基因即为最短步数。

解题步骤:1. 基因库转为集合,快速查重;2. 队列存储当前基因与步数,哈希集合标记已访问;3. 逐位替换四种碱基,生成新基因;4. 新基因等于目标值返回步数+1,在基因库且未访问则入队。

复杂度:时间 O(n4len),空间 O(n)

优缺点:字符串图论BFS经典题,模板通用性强。

易错点总结:未标记已访问导致循环;未校验基因库合法性。

127. 单词接龙(困难)

题目简述:给定起始单词、目标单词与单词列表,每次仅替换一个字符,求最短转换序列长度,无合法序列返回0。

解题思路一:单向BFS(基础可行解)

核心思路:复用基因变化BFS模板,逐层替换字符生成新单词,首次抵达目标单词即为最短路径。

复杂度:时间 O(n*L²),L为单词长度;空间 O(n)

优缺点:逻辑简单;大数据量遍历效率低。

解题思路二:双向BFS(最优优化解)

核心思路:从起点、终点同时向中间层序遍历,两边相遇即得到最短路径,大幅减少遍历节点数量。

复杂度:时间复杂度指数级优化,远优于单向BFS

优缺点:困难题最优解法,面试高频优化考点;代码稍复杂。

易错点总结:重复遍历单词;双向BFS相遇判断逻辑错误;边界空值未处理。

十二、字典树(Trie)专题

字典树核心解题总纲:字典树是处理字符串前缀匹配、检索、模糊匹配的专属树形数据结构,核心优势为公共前缀节点共享,检索效率仅与字符串长度相关。核心解题逻辑:节点存储字符映射+单词结束标记;常规题型为基础增删查、通配符模糊匹配、结合网格DFS前缀剪枝,是字符串算法高频考点。

208. 实现 Trie(前缀树)(中等)

题目简述:手动实现前缀树数据结构,支持三种核心操作:insert插入单词、search精确匹配完整单词、startsWith判断是否存在指定前缀,所有单词仅包含小写英文字母。

解题思路一:数组式Trie(面试标准最优模板)

核心思路:每个节点开辟固定26位数组对应26个小写字母,通过数组下标映射字符,搭配布尔标记区分普通前缀和完整单词,寻址速度快、无哈希冲突,是考场首选模板。

解题步骤:1. 自定义Trie节点结构,包含26个子节点数组、单词结束标记位;2. 初始化空根节点作为检索起点;3. 插入单词:从根节点逐字符遍历,无对应子节点则新建,遍历完成标记末尾节点为单词终点;4. 精确查找:逐字符匹配路径,路径完整且末尾为单词终点才返回true;5. 前缀查找:仅需完整匹配字符路径,无需校验终点标记。

复杂度:插入、查询、前缀匹配均为 O(L),L为单词长度;空间 O(N*L),N为单词总数

优缺点:读写效率高、代码规整、容错率高,适配绝大多数字符串场景;仅支持小写字母,通用性有限。

解题思路二:哈希表式Trie(通用拓展解)

核心思路:节点采用哈希表存储字符与子节点的映射关系,摒弃固定数组限制,可适配大小写字母、数字、特殊符号等任意字符集。

复杂度:单次操作 O(L),空间利用率更高

优缺点:拓展性极强,适配复杂字符场景;哈希寻址存在微小常数开销,速度略慢于数组写法。

易错点总结:精确查找遗漏单词结束标记,误将前缀判定为完整单词;插入字符时未新建节点导致空指针报错;前缀查询错误校验结束标记,导致匹配失效。

示例测试:insert("apple"),search("apple")→true,search("app")→false,startsWith("app")→true

211. 添加与搜索单词 - 数据结构设计(中等)

题目简述:设计数据结构,支持单词添加与模糊搜索。搜索时通配符 . 可匹配任意单个小写字母,实现含通配符的模糊检索功能。

解题思路一:Trie+DFS模糊匹配(满分标准解)

核心思路:底层依托标准数组Trie存储所有单词,遇到普通字符精准匹配路径,遇到通配符时DFS遍历当前节点所有非空子节点,只要存在一条合法匹配路径即判定匹配成功。

解题步骤:1. 复用208题Trie模板实现单词插入功能;2. 自定义DFS递归匹配函数,传入当前节点与字符遍历下标;3. 下标遍历至单词末尾,返回当前节点是否为单词终点;4. 当前为普通字母,精准匹配对应子节点,递归匹配下一字符;5. 当前为通配符,遍历所有26个子节点,任意子节点递归匹配成功即返回true。

复杂度:插入 O(L);最坏搜索 O(26^L),常规数据可高效剪枝;空间 O(N*L)

优缺点:完美适配模糊匹配场景,是本题最优解法,面试高频设计题模板;极端全通配符场景效率较低。

解题思路二:按长度哈希分组(朴素偷懒解)

核心思路:用哈希表按照单词长度分组存储所有插入的单词,搜索时仅遍历同长度单词,逐位比对字符与通配符,无需构建字典树。

复杂度:插入 O(L);最坏搜索 O(N*L)

优缺点:代码极简、上手零难度;单词数量庞大时遍历效率极低,易超时。

易错点总结:通配符场景未遍历所有子节点,遗漏合法路径;递归终止条件设置错误;未处理空字符串、全通配符等边界场景。

212. 单词搜索 II(困难)

题目简述:给定二维字符网格与单词字典,找出网格中所有可由上下左右相邻单元格拼接而成的单词,同一单元格字符不可重复使用。

解题思路一:Trie+网格DFS回溯+前缀剪枝(最优满分解)

核心思路:先通过字典单词构建前缀树,以Trie作为检索依据,在二维网格中逐点启动DFS回溯,依托前缀特性剪枝无效路径,匹配完整单词后记录结果并去重,彻底解决暴力搜索超时问题。

解题步骤:1. 遍历字典所有单词,批量构建Trie前缀树;2. 遍历网格每一个单元格,以当前字符为起点开启DFS;3. DFS中临时修改网格字符标记已访问,防止单元格重复使用;4. 匹配Trie完整单词节点时收录结果,同时对Trie剪枝避免重复检索;5. 递归回溯恢复网格原字符状态,继续枚举其他路径。

复杂度:依托前缀剪枝大幅减少无效递归,远优于暴力DFS;空间由字典单词规模决定

优缺点:本题唯一最优解法,困难算法标杆模板,兼顾检索效率与路径枚举;代码综合性强,需熟练掌握Trie与回溯结合逻辑。

解题思路二:暴力网格DFS+哈希去重(朴素超时解)

核心思路:遍历网格所有起点,DFS枚举所有可能的字符组合,通过哈希表匹配字典单词并去重,无任何前缀剪枝优化。

复杂度:指数级时间复杂度,大数据量必然超时

优缺点:逻辑简单易懂;仅能通过样例测试,无法AC正式数据。

易错点总结:网格访问状态未回溯恢复,导致路径锁定;结果未去重出现重复单词;前缀路径不合法时未及时剪枝,产生大量无效递归;网格越界判断缺失。

示例测试:board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"],输出 ["eat","oath"]

十三、回溯算法专题

回溯算法核心解题总纲:回溯本质是深度优先枚举+状态撤销+剪枝优化,核心逻辑为「选择-递归-回溯」。四大核心题型:组合、排列、子集、网格搜索;核心技巧:有序枚举去重、状态标记回溯、条件剪枝、边界约束,是算法面试必考核心模块。

17. 电话号码的字母组合(中等)

题目简述:给定仅包含2-9的数字字符串,按照手机九宫格键盘映射规则,返回所有可能的字母组合。

解题思路一:DFS分层回溯(组合入门万能模板)

核心思路:建立数字与字母的映射数组,按数字顺序分层递归枚举,每层选取当前数字对应的任意字母,拼接字符完成组合,递归到底收录结果,自动回溯枚举下一种可能。

解题步骤:1. 初始化九宫格数字字母映射表;2. 定义回溯函数,记录当前遍历数字下标与临时组合字符串;3. 下标等于数字字符串长度,收录结果并返回;4. 遍历当前数字对应的所有字母,拼接字母后递归下一层;5. 递归结束自动回溯,无需手动撤销,继续枚举其他字母组合。

复杂度:时间 O(3^m * 4^n),m为3字母数字个数,n为4字母数字个数;空间 O(m+n)

优缺点:回溯入门基础模板,逻辑极简,适配所有分层组合枚举场景,上手极易。

解题思路二:队列迭代拼接(非递归拓展解)

核心思路:借助队列迭代,逐层基于已有字母组合拼接新字母,遍历所有数字后完成全部组合枚举。

复杂度:与递归解题思路一致

优缺点:无递归栈溢出风险;代码繁琐冗余,不推荐面试使用。

易错点总结:空数字字符串未做特殊处理;字母遍历范围错误;结果收录时机偏差,提前或滞后收录。

示例测试:digits = "23",输出 ["ad","ae","af","bd","be","bf","cd","ce","cf"]

77. 组合(中等)

题目简述:给定整数 n、k,返回 1~n 中所有长度为 k 的不重复组合,组合无序、元素不重复、不计顺序差异。

解题思路一:有序回溯+剪枝(标准组合模板)

核心思路:固定选取起始下标,按从小到大顺序枚举元素,从根源杜绝重复组合;通过剩余元素数量预判剪枝,舍弃无法凑齐长度的无效递归路径。

解题步骤:1. 回溯函数维护选取起始下标、临时组合数组;2. 临时组合长度等于k,收录结果并返回;3. 计算剩余可选元素数量,不足所需长度直接剪枝返回;4. 从起始下标向后遍历,选中元素后递归下一层;5. 递归结束回溯移除当前元素,枚举下一个元素。

复杂度:时间 O(C(n,k)*k);空间 O(k)

优缺点:组合问题通用模板,有序枚举彻底去重,剪枝优化高效,考场首选。

解题思路二:无剪枝暴力回溯(朴素解)

核心思路:依靠有序枚举规避重复,不做数量预判剪枝,完整枚举所有可能路径。

复杂度:冗余递归多,大数据量效率低下

优缺点:逻辑简单直观;无优化,易超时。

易错点总结:未固定起始下标,产生重复组合;剪枝公式计算错误;回溯未撤销选中元素,状态混乱。

示例测试:n = 4, k = 2,输出 [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]

46. 全排列(中等)

题目简述:给定无重复数字的数组,返回所有不重复的全排列,元素顺序不同视为不同排列结果。

解题思路一:标记数组回溯(标准排列模板)

核心思路:借助布尔标记数组记录元素使用状态,每层递归选取未使用的元素,填满排列数组后收录结果,递归结束回溯取消标记,枚举其他排列。

解题步骤:1. 初始化与数组等长的标记数组,默认全部未使用;2. 回溯函数维护临时排列数组;3. 临时排列长度等于原数组长度,收录合法结果;4. 遍历所有元素,选取未标记元素,标记为已使用后递归;5. 递归结束回溯,取消元素标记,重置状态。

复杂度:时间 O(n!);空间 O(n)

优缺点:排列问题万能模板,逻辑清晰、零重复解,适配所有无重复排列场景。

解题思路二:原地交换回溯(空间优化解)

核心思路:无需额外标记数组,通过数组原地交换固定前缀元素,递归枚举后缀排列,极致节省额外空间。

复杂度:时间 O(n!);额外空间 O(1)

优缺点:空间最优;交换逻辑抽象,容错率低,不适合新手。

易错点总结:未标记已使用元素,导致排列重复;回溯状态撤销不彻底;结果收录时机错误。

示例测试:nums = [1,2,3],输出全部6种不重复全排列

39. 组合总和(中等)

题目简述:给定无重复元素数组与目标和,数组元素可无限重复选取,找出所有和为目标值的不重复组合。

解题思路一:排序回溯+正向剪枝(最优模板)

核心思路:数组先升序排序,固定选取起始下标保证组合有序不重复;当前元素累加超出目标和时直接剪枝,终止无效递归,大幅提升效率。

解题步骤:1. 数组排序,为剪枝逻辑铺垫;2. 回溯函数维护起始下标、当前累加和、临时组合;3. 累加和等于目标值,收录结果;大于目标值直接返回;4. 从起始下标遍历元素,可重复选取故递归起点不变;5. 递归结束回溯移除元素,尝试下一个元素。

复杂度:时间 O(S),S为所有可行解长度之和;空间 O(target)

优缺点:可重复组合专属模板,排序剪枝高效,彻底规避重复解,适配所有同类型题目。

解题思路二:无排序暴力回溯(朴素解)

核心思路:不排序、不剪枝,仅通过起始下标限制去重,暴力枚举所有组合。

复杂度:冗余递归过多,大数据量易超时

优缺点:逻辑简单;无优化,效率极差。

易错点总结:递归起始下标错误,导致组合重复;未排序无法剪枝引发超时;累加和边界判断错误。

示例测试:candidates = [2,3,6,7], target = 7,输出 [[2,2,3],[7]]

52. N皇后 II(困难)

题目简述:给定整数 n,统计 n×n 棋盘上 N 皇后的合法摆放方案总数,要求任意两个皇后不同行、不同列、不在同一斜线。

解题思路一:行优先回溯+集合剪枝(标准难题模板)

核心思路:逐行放置皇后(规避行冲突),通过三个集合分别记录已占用列、主对角线、副对角线,递归枚举合法位置,不合法路径直接剪枝,遍历完成统计总方案数。

解题步骤:1. 初始化三个集合,存储占用列、主对角线、副对角线;2. 逐行递归放置皇后,遍历完所有行则方案数+1;3. 遍历当前行所有列,校验列与对角线是否被占用;4. 合法位置标记占用状态,递归放置下一行皇后;5. 递归结束回溯,清除占用标记,继续枚举其他位置。

复杂度:时间 O(n!);空间 O(n)

优缺点:N皇后经典解法,剪枝逻辑严谨,代码易懂、容错率高,面试必背难题模板。

解题思路二:位运算状态压缩(极致优化解)

核心思路:用二进制位运算替代集合存储占用状态,压缩空间与时间常数,极致优化运行效率。

复杂度:时间 O(n!),常数极小;空间 O(1)

优缺点:效率拉满;位运算逻辑抽象,手撕难度高,不适合新手。

易错点总结:对角线判定规则错误;状态回溯不彻底导致残留标记;行优先遍历逻辑混乱,造成方案重复统计。

22. 括号生成(中等)

题目简述:给定整数 n,生成所有长度为 2n 的有效括号组合,有效括号需满足:任意前缀左括号数≥右括号数,总左右括号数量相等。

解题思路一:条件约束回溯(括号万能模板)

核心思路:通过两大核心条件剪枝枚举:左括号总数不超过n、右括号总数不超过左括号数,精准过滤所有非法括号组合,仅递归生成合法序列。

解题步骤:1. 回溯函数维护当前左、右括号数量与临时字符串;2. 左右括号均为n个,收录合法结果并返回;3. 左括号数量不足n,优先添加左括号递归;4. 右括号数量小于左括号,可添加右括号递归;5. 递归回溯枚举所有合法组合。

复杂度:时间 O(C(2n,n))(卡特兰数);空间 O(n)

优缺点:最优简洁解法,剪枝精准无无效枚举,代码固定通用,面试高频手撕模板。

解题思路二:动态规划(拓展思路解)

核心思路:依托卡特兰数递推公式,由小规模合法括号组合推导大规模结果,拆分括号子问题求解。

复杂度:与回溯解题思路一致

优缺点:拓展算法思维;代码复杂度高,不适合快速刷题复盘。

易错点总结:右括号数量大于左括号,生成非法序列;递归终止条件错误;缺少剪枝条件,产生大量无效枚举。

示例测试:n = 3,输出 ["((()))","(()())","(())()","()(())","()()()"]

79. 单词搜索(中等)

题目简述:给定二维字符网格与目标单词,判断单词是否可由网格上下左右相邻单元格字符拼接而成,同一单元格不可重复使用。

解题思路一:网格DFS回溯+原位标记(标准模板)

核心思路:遍历网格所有字符作为起点,四向深度优先匹配单词字符,通过临时修改网格字符标记已访问路径,递归结束回溯恢复原字符,无需额外空间存储访问状态。

解题步骤:1. 双层循环遍历网格所有单元格,匹配单词首字符则启动DFS;2. DFS终止条件:字符下标匹配完成,返回匹配成功;坐标越界、字符不匹配,返回失败;3. 临时保存当前网格字符,修改为占位符标记已访问;4. 四向递归匹配下一字符,任意方向匹配成功即返回true;5. 回溯恢复单元格原字符,继续枚举其他路径。

复杂度:时间 O(mn4^L),m、n为网格行列数,L为单词长度;空间 O(L)

优缺点:二维网格回溯通用模板,原位标记节省空间,逻辑清晰、适配所有网格字符匹配题型。

解题思路二:标记数组回溯(朴素解)

核心思路:额外开辟二维布尔数组记录单元格访问状态,DFS匹配字符,递归结束取消标记回溯状态。

复杂度:时间复杂度一致;空间 O(m*n)

优缺点:不修改原网格数据;占用额外空间,效率略低。

易错点总结:网格越界判断缺失;访问状态未回溯恢复;字符匹配顺序错误;提前终止递归导致遗漏路径。

十四、分治算法专题

分治算法核心解题总纲:分治核心思想为分、治、合,将大问题拆解为若干同质子问题,递归求解子问题,最终合并子问题结果得到原问题答案。适用于结构可拆分、子问题独立、结果可合并的题型,高频场景:有序结构构造、链表归并、二维区域处理、多路合并。

108. 将有序数组转换为二叉搜索树(简单)

题目简述:给定一个严格递增的有序整数数组,将其转换为一棵高度平衡的二叉搜索树,平衡树要求左右子树高度差不超过1。

解题思路一:中点分治递归(平衡BST标准模板)

核心思路:利用有序数组对应BST中序序列的特性,每次选取数组中点作为当前根节点,保证左右元素数量均衡,天然形成平衡结构,再递归分治构造左右子树。

解题步骤:1. 设定左右边界,区间无效则返回空节点;2. 取当前区间中点元素作为根节点;3. 左区间递归构造左子树,右区间递归构造右子树;4. 返回当前根节点,逐层合并子树。

复杂度:时间 O(n),遍历所有元素一次;空间 O(logn),递归栈为树高

优缺点:代码极简、逻辑严谨,生成树严格平衡,是有序数组建BST最优解。

解题思路二:暴力构建(朴素非平衡解)

核心思路:依次遍历数组元素,逐个插入BST,仅保证BST性质,无法保证平衡。

复杂度:时间 O(n²),最坏链式结构;空间 O(n)

优缺点:不符合题目平衡要求,仅作思路拓展,不可用于解题。

易错点总结:选取左中点/右中点不当导致树不平衡;递归边界处理错误造成数组越界;区间划分重叠或遗漏元素。

示例测试:nums = [-10,-3,0,5,9],输出平衡BST,根节点为0,左右子树对称分布

148. 排序链表(中等)

题目简述:对无序单链表进行升序排序,要求时间复杂度 O(nlogn),空间复杂度 O(1)。

解题思路一:自底向上归并分治(空间最优解)

核心思路:链表最优排序算法,依托归并排序分治思想,自底向上逐段拆分、两两合并有序链表,无递归栈开销,满足常数空间要求。

解题步骤:1. 统计链表总长度,确定归并分段步长;2. 按步长拆分链表,分割出前后两段独立子链;3. 合并两个有序子链表,拼接至结果链表;4. 步长翻倍迭代,重复拆分合并,直至整链有序。

复杂度:时间 O(nlogn);空间 O(1)

优缺点:完全符合题目复杂度限制,面试链表排序最优模板;逻辑细节较多,边界处理繁琐。

解题思路二:自顶向下递归归并(直观易懂解)

核心思路:快慢指针找到链表中点,拆分左右两个子链,递归排序子链,最后合并两个有序链表。

复杂度:时间 O(nlogn);空间 O(logn),递归栈开销

优缺点:逻辑清晰、易手撕;不满足常数空间最优要求。

易错点总结:快慢指针找中点未断链,导致链表环;合并链表指针操作混乱;边界空链表未处理。

427. 建立四叉树(中等)

题目简述:给定 n×n 二维网格,构建四叉树。网格值全0/全1为叶子节点,否则递归均分为四个子区域,构建非叶子节点。

解题思路一:二维区域分治递归(标准模板)

核心思路:二维分治核心题型,先校验当前区域数值是否完全统一,统一则直接创建叶子节点,否则四分区域递归构造四个子节点。

解题步骤:1. 确定当前区域的行列边界,遍历校验区域数值是否一致;2. 数值统一,创建叶子节点返回;3. 数值不统一,计算区域中点,均分左上、右上、左下、右下四块;4. 递归构造四个子节点,创建非叶子节点挂载子节点返回。

复杂度:时间 O(n²logn);空间 O(logn)递归栈

优缺点:二维分治经典模板,适配所有区域拆分递归题型,逻辑通用。

易错点总结:区域边界划分错误,子区域重叠或缺漏;叶子节点判定条件错误;递归终止条件缺失。

23. 合并K个升序链表(困难)

题目简述:给定多个升序排列的单链表,将其合并为一个升序有序链表。

解题思路一:分治两两合并(最优分治解)

核心思路:分治拆分链表数组,将k个链表拆分为两组,递归合并左右两组链表,最终两两合并得到全局有序链表。

解题步骤:1. 设定数组左右边界,边界重合直接返回当前链表;2. 取中点拆分链表数组为左右两部分;3. 递归合并左区间、右区间所有链表;4. 合并左右两个有序结果链表并返回。

复杂度:时间 O(nlogk),n为总节点数,k为链表数;空间 O(logk)

优缺点:时间复杂度最优,分治标准解法,面试首选。

解题思路二:小顶堆多路归并(堆解法)

核心思路:借助小顶堆存储各链表当前头节点,每次取出堆顶最小节点,并入对应链表下一节点,迭代完成合并。

复杂度:时间 O(nlogk);空间 O(k)

优缺点:迭代实现无递归风险;需要额外堆空间,空间略逊于分治。

易错点总结:空链表未过滤导致报错;分治区间拆分错误;堆排序节点比较逻辑缺失。

十五、Kadane算法(最大子数组专题)

Kadane算法核心解题总纲:Kadane算法是求解最大子数组和的专属贪心+DP算法,核心思想:遍历过程中维护「以当前元素结尾的最大子数组和」,抉择「继承前面子数组」或「重新开启子数组」,线性时间解决子数组最值问题,延伸适配环形子数组场景。

53. 最大子数组和(中等)

题目简述:给定整数数组,找出和最大的连续子数组,返回其最大和,子数组最少包含一个元素。

解题思路一:标准Kadane算法(最优模板)

核心思路:维护当前局部最大和、全局最大和,对每个元素,选择「当前元素单独起始」或「接在前一子数组后」,动态更新最值。

解题步骤:1. 初始化局部和、全局和为数组首元素;2. 从第二个元素开始遍历;3. 局部和 = max(nums[i], 局部和 + nums[i]);4. 全局和实时更新为最大值;5. 遍历结束返回全局和。

复杂度:时间 O(n);空间 O(1)

优缺点:线性遍历、常数空间,本题最优解,万能子数组最值模板。

解题思路二:动态规划朴素解

核心思路:定义dp[i]为以i结尾的最大子数组和,状态转移:dp[i] = max(nums[i], dp[i-1]+nums[i]),最终取dp数组最大值。

复杂度:时间 O(n);空间 O(n)

优缺点:逻辑直观易懂;占用额外数组空间,可空间压缩为Kadane写法。

易错点总结:初始化为0,无法适配全负数数组;局部、全局最值更新顺序错误。

示例测试:nums = [-2,1,-3,4,-1,2,1,-5,4],输出最大和 6(子数组[4,-1,2,1])

918. 环形子数组的最大和(中等)

题目简述:给定环形整数数组,数组首尾相连,求环形连续子数组的最大和。

解题思路一:Kadane变形+正反遍历(满分模板)

核心思路:环形子数组分两种情况:1. 最大子数组在数组中间,直接用标准Kadane求解;2. 最大子数组首尾相连,等价于「数组总和 - 中间最小子数组和」。最终取两种情况最大值,全负数时直接返回标准最大子数组和。

解题步骤:1. 标准Kadane求非环形最大子数组和maxSum;2. 反向Kadane求最小子数组和minSum;3. 计算数组总和total,环形最大值为 total - minSum;4. 特殊判断:全负数直接返回maxSum,否则返回两种情况最大值。

复杂度:时间 O(n);空间 O(1)

优缺点:线性遍历一次解决环形问题,技巧性极强,环形子数组专属模板。

易错点总结:未处理全负数特殊场景,导致结果错误;最小子数组和计算逻辑偏差;总和相减逻辑混淆。

十六、二分查找专题

二分查找核心解题总纲:二分核心是边界收缩,通过判断中间值,每次舍弃一半无效区间,将O(n)遍历降为O(logn)。四大题型:有序查找、边界查找、无序极值查找、旋转有序数组查找,核心难点在于左右边界更新、循环条件、返回值区分。

35. 搜索插入位置(简单)

题目简述:给定升序无重复数组与目标值,找到目标值下标,不存在则返回有序插入位置。

解题思路一:左边界标准二分(基础模板)

核心思路:经典左边界二分模板,不断收缩右边界锁定大于等于目标值的第一个位置,天然适配插入位置需求。

解题步骤:1. 初始化左右边界0、数组长度;2. 循环二分,中间值等于目标值收缩右边界;3. 中间值小于目标值,左边界右移;4. 中间值大于目标值,右边界左移;5. 最终左边界即为答案。

复杂度:时间 O(logn);空间 O(1)

优缺点:二分入门万能模板,适配所有左边界查找场景。

易错点总结:循环条件、边界更新错误;返回右边界导致结果偏差。

74. 搜索二维矩阵(中等)

题目简述:给定每行升序、每行首元素大于上一行尾元素的二维矩阵,判断目标值是否存在。

解题思路一:一维映射二分(最优解)

核心思路:二维矩阵可视为一维有序数组,通过坐标映射公式,将一维下标转为二维行列坐标,直接套用标准二分模板。

解题步骤:1. 计算矩阵总元素数,设定一维二分边界;2. 一维中间下标映射:row = mid / 列数,col = mid % 列数;3. 比较映射值与目标值,收缩左右边界;4. 匹配成功返回true,否则返回false。

复杂度:时间 O(log(mn));空间 O(1)

优缺点:代码简洁、复杂度最优,二维有序矩阵专属二分技巧。

解题思路二:先行后列二分(朴素解)

核心思路:先二分确定目标值所在行,再在行内二分查找目标值。

复杂度:时间 O(logm + logn)

优缺点:逻辑直观;两次二分略繁琐。

162. 寻找峰值(中等)

题目简述:给定无序数组,峰值元素大于左右相邻元素,数组首尾边界视为无穷小,返回任意一个峰值下标。

解题思路一:无序二分爬坡(极值模板)

核心思路:无序数组亦可二分,比较中间值与右侧值,向数值更大的一侧爬坡,该侧必然存在峰值,不断收缩区间最终锁定峰值。

复杂度:时间 O(logn);空间 O(1)

优缺点:打破二分必须有序的固有认知,无序极值查找经典模板。

易错点总结:双向判断冗余;爬坡方向颠倒导致错过峰值。

33. 搜索旋转排序数组(中等)

题目简述:升序数组旋转后分为两段有序区间,无重复元素,查找目标值是否存在。

解题思路一:分段有序二分(旋转数组模板)

核心思路:每次二分必然存在一段有序区间,先判断左/右区间有序,再判断目标值是否落在有序区间内,精准收缩边界。

解题步骤:1. 二分取中间值,匹配目标直接返回下标;2. 判断左区间有序,目标在左区间则收缩右边界,否则走右区间;3. 右区间有序,目标在右区间则收缩左边界,否则走左区间;4. 无匹配返回-1。

复杂度:时间 O(logn);空间 O(1)

优缺点:旋转数组核心模板,逻辑通用,适配所有无重复旋转有序数组查找。

34. 在排序数组中查找元素的第一个和最后一个位置(中等)

题目简述:升序数组含重复元素,返回目标值的首尾下标,不存在返回[-1,-1]。

解题思路一:两次边界二分(标准模板)

核心思路:拆分两次二分,分别实现左边界查找右边界查找,最后校验边界合法性返回结果。

复杂度:时间 O(logn);空间 O(1)

优缺点:边界二分标杆模板,彻底区分左右边界收缩逻辑,面试高频。

易错点总结:左右边界更新逻辑混淆;未校验边界越界、数值不匹配场景。

153. 寻找旋转排序数组中的最小值(中等)

题目简述:无重复升序数组旋转后,找出数组最小值。

解题思路一:右边界对比二分(最优模板)

核心思路:始终比较中间值与右边界值,中间值大于右边界,说明最小值在右侧无序区间;反之最小值在左侧区间,持续收缩锁定最小值。

复杂度:时间 O(logn);空间 O(1)

优缺点:旋转数组找最值专属模板,逻辑简洁、无冗余判断。

4. 寻找两个正序数组的中位数(困难)

题目简述:给定两个正序数组,求合并后数组的中位数,要求时间复杂度 O(log(m+n))。

解题思路一:数组二分分割(高阶最优解)

核心思路:不合并数组,通过二分枚举短数组分割点,将两个数组分为左右两部分,保证左半部分总数等于右半部分,且左半最大值≤右半最小值,据此计算中位数。

复杂度:时间 O(log(min(m,n)));空间 O(1)

优缺点:严格满足复杂度要求,二分高阶难题模板,面试压轴高频题。

解题思路二:合并排序(朴素超时解)

核心思路:合并两个数组后排序取中位数。

复杂度:时间 O(m+n),不满足题目要求

优缺点:仅能过样例,不符合高阶算法要求。

十七、堆(优先队列)专题

堆算法核心解题总纲:堆是完全二叉树实现的优先队列,核心用于动态维护最值、TopK问题、多路归并、贪心筛选。解题核心技巧:小顶堆存大数筛TopK、大顶堆存小数筛TopK、双堆平衡维护中位数、堆+贪心筛选最优解。

215. 数组中的第K个最大元素(中等)

题目简述:给定无序整数数组,返回数组中第k个最大元素。

解题思路一:小顶堆维护TopK(高频模板)

核心思路:维护大小为k的小顶堆,遍历数组,堆未满直接入堆,堆满后大于堆顶则替换堆顶,最终堆顶即为第k大元素。

复杂度:时间 O(nlogk);空间 O(k)

优缺点:海量数据最优解,无需排序全数组,流式处理适配大数据场景。

解题思路二:快排分治(原地最优解)

核心思路:借助快排分区思想,每次确定基准值最终位置,根据下标收缩区间,直接定位第k大元素。

复杂度:平均 O(n),最坏 O(n²);空间 O(1)

优缺点:平均效率更高;不稳定,极端数据易退化。

示例测试:nums = [3,2,1,5,6,4], k = 2,输出 5

502. IPO(困难)

题目简述:给定初始资本,限定最多完成k个项目,每个项目有启动资本和利润,求最终最大资本。

解题思路一:双堆+贪心(满分模板)

核心思路:小顶堆存储项目资本(筛选可做项目),大顶堆存储可做项目利润(筛选最大利润),每次贪心选择当前资本可启动的最大利润项目,迭代k次。

复杂度:时间 O(nlogn + klogn);空间 O(n)

优缺点:堆+贪心经典难题模板,唯一最优解题思路。

易错点总结:项目筛选顺序错误;未及时更新资本、重复选取项目。

373. 查找和最小的K对数字(中等)

题目简述:两个升序数组,选取k对数字,返回和最小的k个数对。

解题思路一:小顶堆多路归并(最优解)

核心思路:基于数组有序特性,初始化存入最小的数对,每次弹出堆顶最小值,并入下一个合法数对,控制遍历范围避免重复。

复杂度:时间 O(klogk);空间 O(k)

优缺点:多路归并经典模板,高效筛取最优组合,无暴力枚举开销。

295. 数据流的中位数(困难)

题目简述:设计数据结构,支持动态添加数字,实时返回当前数据流的中位数。

解题思路一:大小双堆平衡(流式数据模板)

核心思路:大顶堆存储左半部分小数,小顶堆存储右半部分大数,始终维持两堆大小平衡(差值≤1),奇数取多堆堆顶,偶数取两堆堆顶均值。

解题步骤:1. 新数优先加入大顶堆,再平衡两堆数量;2. 保证大顶堆元素≥小顶堆元素;3. 根据两堆数量奇偶计算中位数。

复杂度:插入 O(logn),查找中位数 O(1);空间 O(n)

优缺点:流式中位数最优解法,双堆平衡经典模板,面试必背难题。

十八、位运算专题

位运算核心解题总纲:位运算基于二进制底层操作,具有速度快、常数开销极小的特点,是算法优化核心手段。核心常用性质:异或相同为0、不同为1(抵消特性)、n&(n-1)消去二进制末尾1、移位实现乘除2、按位与/或/异或完成二进制运算。高频题型:二进制运算、位数统计、特殊数字筛选、区间位运算化简。

67. 二进制求和(简单)

题目简述:给定两个二进制字符串,返回二者相加的二进制字符串结果,不使用内置进制转换函数。

解题思路一:模拟进位+位运算(标准最优模板)

核心思路:双指针从两个字符串末尾逆向遍历,逐位取出二进制数值,结合进位完成加法运算,通过位运算快速计算当前位结果与新进位,最后反转结果字符串得到答案。

解题步骤:1. 初始化双指针分别指向两个字符串末尾,进位初始化为0;2. 循环遍历至两个字符串遍历完毕且进位为0;3. 逐位累加两个字符串当前位数值与进位;4. 当前位结果为总和%2,新进位为总和//2;5. 拼接结果字符,遍历结束后反转字符串输出。

复杂度:时间 O(max(m,n)),m、n为两个字符串长度;空间 O(1),不计结果存储空间

优缺点:纯模拟底层运算,贴合题目要求,无内置函数依赖,面试标准手撕模板。

解题思路二:内置函数转换(朴素偷懒解)

核心思路:通过内置函数将二进制字符串转为十进制整数,求和后再转回二进制字符串,截取有效结果。

复杂度:时间 O(m+n);空间 O(1)

优缺点:代码极简;违背题目考察底层运算的初衷,面试不推荐使用。

易错点总结:忘记处理最后剩余进位;遍历方向错误;结果未反转导致输出颠倒;空字符串边界未处理。

示例测试:a = "11", b = "1",输出 "100"

190. 颠倒二进制位(简单)

题目简述:给定一个32位无符号整数,将其二进制位首尾颠倒,返回颠倒后的整数结果。

解题思路一:逐位移位拼接(基础最优模板)

核心思路:循环遍历32位二进制位,每次取出原数字末尾一位,将结果数字左移一位腾出空位,拼接当前取出的二进制位,逐位完成颠倒。

解题步骤:1. 初始化结果为0,循环32次遍历所有二进制位;2. 结果左移一位,空出最低位;3. 取出原数字最低位,与结果做或运算完成拼接;4. 原数字右移一位,舍弃已处理的最低位;5. 循环结束返回结果。

复杂度:时间 O(1),固定遍历32次;空间 O(1)

优缺点:位运算基础标杆模板,逻辑简洁、常数级复杂度,适配所有位颠倒场景。

解题思路二:分块交换(进阶优化解)

核心思路:通过分块交换二进制位(16位、8位、4位、2位、1位),快速完成整体颠倒,减少循环次数。

复杂度:时间 O(1);空间 O(1)

优缺点:效率极致;逻辑抽象,新手不易理解,面试常规不使用。

易错点总结:循环次数不足32位,导致高位缺失;移位顺序颠倒,拼接结果错误;未处理无符号整数边界。

191. 位1的个数(简单)

题目简述:给定一个无符号整数,统计其二进制表示中1的个数,也称为汉明重量。

解题思路一:n&(n-1)消位计数(最优解)

核心思路:核心位运算技巧:n & (n-1) 可以精准消去n二进制中最右侧的一个1,循环执行该操作,统计执行次数即为1的个数。

解题步骤:1. 初始化计数器为0;2. 当n不为0时,执行n = n & (n-1);3. 每次操作后计数器+1;4. n归0后返回计数器结果。

复杂度:时间 O(k),k为二进制中1的个数,远优于遍历32位;空间 O(1)

优缺点:最优时间复杂度,位运算经典技巧,面试高频考点。

解题思路二:逐位遍历校验(朴素解)

核心思路:循环32次,每次通过与运算校验最低位是否为1,统计所有1的数量。

复杂度:时间 O(32)=O(1);空间 O(1)

优缺点:逻辑简单直观;固定遍历32次,效率略低于消位法。

易错点总结:循环终止条件错误;混淆有符号与无符号整数运算规则。

示例测试:n = 11(二进制1011),输出 3

136. 只出现一次的数字(简单)

题目简述:给定非空整数数组,除某个元素仅出现一次外,其余所有元素均出现两次,找出这个唯一的单次元素。

解题思路一:异或运算(专属最优模板)

核心思路:依托异或核心性质:a^a=0、a^0=a、异或运算满足交换律结合律。所有成对出现的数字异或抵消为0,最终剩余值即为唯一单次数字。

解题步骤:1. 初始化结果为0;2. 遍历数组所有元素,依次与结果异或;3. 遍历结束,结果即为唯一数。

复杂度:时间 O(n);空间 O(1)

优缺点:线性遍历、常数空间,本题唯一最优解,异或经典入门模板。

解题思路二:哈希表统计(朴素解)

核心思路:哈希表记录每个数字出现次数,遍历哈希表找出次数为1的数字。

复杂度:时间 O(n);空间 O(n)

优缺点:逻辑易懂;占用额外空间,不满足最优空间要求。

易错点总结:异或初始值错误;未利用交换律导致逻辑混乱。

示例测试:nums = [2,2,1],输出 1

137. 只出现一次的数字 II(中等)

题目简述:给定整数数组,除某个元素仅出现一次外,其余所有元素均出现三次,找出这个唯一元素。

解题思路一:位计数取模+有限状态机(满分模板)

核心思路:统计每一位二进制位出现的总次数,由于重复数字均出现三次,每一位次数对3取模,余数即为唯一数字对应二进制位的值;进阶可通过有限状态机模拟0/1/2次状态切换,高效统计结果。

解题步骤:1. 遍历32个二进制位,逐位统计数组所有数字该位的1的个数;2. 个数%3不为0,说明唯一数字该位为1,否则为0;3. 拼接所有二进制位,得到最终结果。

复杂度:时间 O(32n)=O(n);空间 O(1)

优缺点:常数空间线性时间,位运算高阶技巧,通用适配「其余数字出现k次」题型。

解题思路二:哈希表统计(朴素解)

核心思路:哈希表统计数字出现次数,筛选单次数字。

复杂度:时间 O(n);空间 O(n)

优缺点:简单直观;空间复杂度不最优,无法体现位运算特性。

易错点总结:二进制位遍历不全;取模逻辑错误;正负整数位运算边界处理不当。

示例测试:nums = [0,1,0,1,0,1,99],输出 99

201. 数字范围按位与(中等)

题目简述:给定两个整数 left 和 right,返回 [left, right] 区间内所有数字按位与的结果。

解题思路一:公共前缀消除法(最优技巧模板)

核心思路:区间内数字连续变化,按位与结果的有效位仅为所有数字的二进制公共前缀,后缀不同位全部会被抵消为0。不断右移两个数字,直至二者相等,统计移位次数,最后左移还原结果。

解题步骤:1. 初始化移位计数器为0;2. 当left≠right时,两数同时右移一位,计数器+1;3. 两数相等时,得到二进制公共前缀;4. 将公共前缀左移对应次数,还原后缀0,得到最终结果。

复杂度:时间 O(logn),移位次数为二进制位数;空间 O(1)

优缺点:极致优化,避开暴力遍历区间,位运算经典思维题模板。

解题思路二:暴力遍历按位与(朴素超时解)

核心思路:遍历区间所有数字,依次累积按位与结果。

复杂度:时间 O(right-left+1),大数区间极易超时

优缺点:逻辑直白;数据范围大时效率极差,无法AC。

易错点总结:移位次数统计错误;左右边界判断逻辑颠倒;未理解公共前缀核心原理。

示例测试:left = 5, right = 7,输出 4

十九、数学专题

数学专题核心解题总纲:算法数学题型核心为数学规律推导、数值模拟、迭代化简、几何特性,无需复杂数据结构,重点考察思维优化与边界处理。高频题型:数值反转、进位模拟、因式统计、快速幂、二分迭代、几何统计,核心技巧为规避字符串转换、找规律剪枝、避免数值溢出。

9. 回文数(简单)

题目简述:判断一个整数是否是回文数,正读和反读数值一致,要求不转换为字符串解题。

解题思路一:反转后半数值(最优模板)

核心思路:不转换字符串,通过取余、除法反转数字后半部分,仅反转一半数值即可完成判断,避免全量反转溢出问题,同时提升效率。

解题步骤:1. 负数、末尾为0的非0数直接判定非回文;2. 循环取出数字末尾位,拼接反转后半数值;3. 当原数字小于等于反转数值时,完成一半反转;4. 偶数位:原数==反转数;奇数位:原数==反转数//10。

复杂度:时间 O(logn),遍历数字位数一半;空间 O(1)

优缺点:无溢出风险、常数空间,完全满足题目限制,面试标准解法。

解题思路二:全量反转对比(朴素解)

核心思路:完整反转整个数字,对比反转前后数值是否一致。

复杂度:时间 O(logn);空间 O(1)

优缺点:逻辑简单;存在数值溢出风险,边界处理繁琐。

易错点总结:未处理负数、末尾0边界;全量反转溢出;奇偶位数判断错误。

示例测试:x = 121,输出 true;x = -121,输出 false

66. 加一(简单)

题目简述:给定由整数组成的数组表示大整数,数组每位对应整数一位,返回该大整数加一后的数组结果。

解题思路一:逆序遍历进位模拟(极简最优模板)

核心思路:从数组末尾逆序遍历,模拟加法进位,当前位小于9直接加一终止遍历,等于9则置0并继续进位,全9情况首位补1。

解题步骤:1. 逆序遍历数组每一位;2. 当前位数值≠9,直接+1,返回数组;3. 当前位=9,置0,进位延续;4. 遍历完成说明全为9,数组头部插入1返回。

复杂度:时间 O(n);空间 O(1),不计结果扩容空间

优缺点:逻辑极简、遍历次数最少,无冗余操作,经典模拟题模板。

易错点总结:进位传递处理不全;全9边界场景遗漏;正序遍历逻辑混乱。

示例测试:digits = [1,2,3],输出 [1,2,4];digits = [9,9],输出 [1,0,0]

172. 阶乘后的零(中等)

题目简述:给定整数n,返回n! 结果末尾0的个数,不直接计算阶乘(避免大数溢出)。

解题思路一:统计5的因子数(数学规律最优解)

核心思路:末尾0由 2*5 产生,阶乘中2的因子数量远多于5,因此末尾0的个数等价于阶乘中5的因子总个数,不断整除5累加计数即可。

解题步骤:1. 初始化计数器为0;2. 当n>0时,n = n//5,累加n至计数器;3. 循环结束返回计数器。

复杂度:时间 O(log₅n);空间 O(1)

优缺点:极致高效,纯数学规律解题,彻底规避大数溢出问题。

易错点总结:仅统计单次5的倍数,遗漏25、125等多5因子数字;循环终止条件错误。

示例测试:n = 10,输出 2(10! = 3628800)

69. x 的平方根(简单)

题目简述:给定非负整数x,返回x的算术平方根的整数部分,舍弃小数部分,不使用内置开方函数。

解题思路一:二分查找(稳定最优模板)

核心思路:在[0,x]区间二分查找平方值≤x的最大整数,通过除法替代乘法避免数值溢出,边界收缩锁定答案。

解题步骤:1. 初始化左右边界0、x;2. 二分取中间值,通过 mid <= x/mid 判断平方关系;3. 满足条件则更新答案,右移左边界;4. 不满足则左移右边界;5. 遍历结束返回最优答案。

复杂度:时间 O(logn);空间 O(1)

优缺点:数值稳定无溢出,逻辑严谨,面试首选通用解法。

解题思路二:牛顿迭代法(极速优化解)

核心思路:利用牛顿迭代公式快速逼近平方根精确值,迭代收敛后取整数部分。

复杂度:时间 O(logn),收敛速度快于二分;空间 O(1)

优缺点:迭代次数极少效率极高;公式抽象,新手不易掌握。

易错点总结:乘法判断导致溢出;边界答案更新时机错误;0、1特殊值未处理。

示例测试:x = 8,输出 2;x = 4,输出 2

50. Pow(x, n)(中等)

题目简述:实现幂函数,计算x的n次幂,n为32位有符号整数,要求高效计算。

解题思路一:快速幂(分治迭代模板)

核心思路:分治思想,将指数对半拆分,通过迭代快速幂实现,每次将指数折半,底数平方,奇偶指数单独处理,时间复杂度指数级优化。

解题步骤:1. 处理负指数:底数取倒数,指数取绝对值;2. 初始化结果为1;3. 指数大于0时,奇数指数则结果乘当前底数;4. 底数平方,指数右移折半;5. 迭代结束返回结果。

复杂度:时间 O(log|n|);空间 O(1)

优缺点:迭代无递归栈开销,高效稳定,快速幂通用万能模板。

解题思路二:递归快速幂(直观解)

核心思路:递归拆分指数,偶数幂拆分两半相乘,奇数幂拆分后多乘一次底数。

复杂度:时间 O(log|n|);空间 O(log|n|)递归栈

优缺点:逻辑易懂;存在递归栈开销,极端大数据可能栈溢出。

易错点总结:负指数处理错误;奇偶幂判断偏差;底数更新逻辑错误。

示例测试:x = 2.00000, n = 10,输出 1024.00000

149. 直线上最多的点数(困难)

题目简述:给定二维平面上若干点,计算最多有多少个点在同一条直线上。

解题思路一:哈希表统计最简斜率(满分模板)

核心思路:固定每个点为基准点,计算其余点与基准点的最简斜率,哈希表统计斜率频次,频次最大值+基准点即为共线点数;同时处理重复点、竖直线特殊场景。

解题步骤:1. 遍历每个点作为基准点,初始化哈希表;2. 统计重复点数量、竖直无斜率点数;3. 计算两点横纵坐标差值,约分得到最简斜率;4. 哈希表记录斜率频次,更新最大共线点数;5. 遍历所有基准点,返回全局最大值。

复杂度:时间 O(n²);空间 O(n)

优缺点:几何数学经典难题模板,精准处理所有边界场景,唯一最优解法。

易错点总结:斜率未约分导致精度误差;重复点统计遗漏;竖直线特殊场景未处理;浮点斜率精度丢失。

二十、一维动态规划

一维DP核心解题总纲:一维动态规划核心是线性状态递推,定义dp[i]为前i个元素/第i个位置的最优解,通过前置状态推导当前状态,无后效性。核心题型:线性递推、不相邻选择、完全背包、字符串拆分、最长子序列,核心技巧:状态压缩、贪心优化、二分优化降维。

70. 爬楼梯(简单)

题目简述:每次可爬1级或2级台阶,求爬上n级台阶的总方法数。

解题思路一:一维线性DP(斐波那契模板)

核心思路:最后一步只能走1级或2级,因此dp[i] = dp[i-1] + dp[i-2],本质为斐波那契数列变形,可通过变量压缩空间。

解题步骤:1. 边界初始化:n=1、n=2直接返回对应值;2. 初始化前两项状态变量;3. 迭代从3到n,递推更新当前状态;4. 迭代结束返回最终结果。

复杂度:时间 O(n);空间 O(1)(状态压缩后)

优缺点:线性递推极简模板,一维DP入门必背,空间极致优化。

解题思路二:递归暴力枚举(朴素超时解)

核心思路:递归拆分1步、2步场景,枚举所有路径。

复杂度:时间 O(2ⁿ),指数级超时;空间 O(n)

优缺点:逻辑直观;大量重复计算,无法通过大数据。

易错点总结:边界条件初始化错误;递推顺序颠倒。

示例测试:n = 3,输出 3

198. 打家劫舍(中等)

题目简述:数组每个元素为房屋金额,不能偷窃相邻房屋,求可偷窃的最大总金额。

解题思路一:状态压缩一维DP(不相邻选择模板)

核心思路:每个房屋有选/不选两种状态,选则不能选前一个,不选则继承前一个最优解,状态转移:dp[i] = max(dp[i-1], dp[i-2]+nums[i]),用两个变量压缩空间。

解题步骤:1. 处理空数组、单元素数组边界;2. 初始化前两位最优状态;3. 遍历后续元素,迭代更新选与不选的最大值;4. 遍历结束返回最终最优值。

复杂度:时间 O(n);空间 O(1)

优缺点:不相邻DP万能模板,空间最优,适配所有线性间隔选择最值问题。

易错点总结:状态转移公式混淆;边界数组未处理;迭代更新顺序错误。

示例测试:nums = [1,2,3,1],输出 4

139. 单词拆分(中等)

题目简述:给定字符串和单词字典,判断字符串是否可被拆分为字典中出现的有效单词组合。

解题思路一:一维字符串DP(标准模板)

核心思路:定义dp[i]为字符串前i个字符能否有效拆分,遍历每个位置,向前截取子串匹配字典,若前置位置合法且子串合法,则当前位置合法。

解题步骤:1. 初始化dp[0]=true,空字符串默认合法;2. 遍历字符串每个结束位置i;3. 向前遍历起始位置j,判断dp[j]合法且[j,i]子串在字典中;4. 满足条件则标记dp[i]=true,跳出循环;5. 返回末尾状态结果。

复杂度:时间 O(n²);空间 O(n)

优缺点:字符串拆分经典DP模板,逻辑通用,适配所有序列拆分问题。

易错点总结:dp初始状态错误;子串截取范围偏差;字典查询效率低未优化。

示例测试:s = "leetcode", wordDict = ["leet","code"],输出 true

322. 零钱兑换(中等)

题目简述:给定不同面额硬币和总金额,可无限使用硬币,凑出总金额的最少硬币个数,无法凑出返回-1。

解题思路一:一维完全背包DP(最优模板)

核心思路:完全背包题型(物品无限选取),定义dp[i]为凑出金额i的最少硬币数,状态转移:dp[i] = min(dp[i], dp[i-coins]+1),初始化为无穷大,迭代更新最小值。

解题步骤:1. 初始化dp数组为无穷大,dp[0]=0;2. 遍历1到总金额所有金额;3. 遍历所有硬币,金额足够则更新dp值;4. 最终值仍为无穷大则返回-1,否则返回结果。

复杂度:时间 O(amount*n);空间 O(amount)

优缺点:完全背包万能入门模板,适配所有无限选取最值问题。

易错点总结:数组初始化值错误;遍历顺序颠倒;无解场景未特殊判断。

示例测试:coins = [1,2,5], amount = 11,输出 3(5+5+1)

300. 最长递增子序列(中等)

题目简述:给定无序整数数组,找出最长严格递增子序列的长度,子序列无需连续。

解题思路一:DP+二分优化(O(nlogn)最优解)

核心思路:维护一个递增数组存储最小末尾元素,遍历数组,当前元素大于末尾则追加,否则二分查找替换首个大于当前元素的位置,最终数组长度即为最长子序列长度。

解题步骤:1. 初始化空结果数组;2. 遍历数组每个元素;3. 元素大于数组末尾,直接追加;4. 否则二分查找替换对应位置元素;5. 遍历结束返回数组长度。

复杂度:时间 O(nlogn);空间 O(n)

优缺点:本题最优复杂度解法,面试高频高阶模板,突破朴素DP平方复杂度。

解题思路二:朴素一维DP(O(n²))

核心思路:dp[i]表示以i结尾的最长递增子序列长度,向前遍历所有前置元素,满足递增则更新最大值。

复杂度:时间 O(n²);空间 O(n)

优缺点:逻辑易懂;大数据量超时,仅适合入门理解。

示例测试:nums = [10,9,2,5,3,7,101,18],输出 4

二十一、多维动态规划

多维DP核心解题总纲:多维DP通过二维/高维状态数组记录多维度状态,解决网格路径、区间子串、双序列匹配、状态约束类问题。核心思想:dp[i][j]代表二维位置/区间/双序列对应的最优解,通过上、左、左上等前置维度状态推导当前状态,高频场景:网格路径、回文区间、字符串匹配、股票多状态、二维最值。

120. 三角形最小路径和(中等)

题目简述:给定三角形二维数组,每次只能移动到下一行相邻节点,求从顶到底的最小路径和。

解题思路一:自底向上原地DP(空间最优模板)

核心思路:从最后一行向上逆推,原地修改数组,当前位置最优值 = 当前值 + min(下一行同列、下一行右列),无需额外DP数组,常数空间。

解题步骤:1. 从倒数第二行开始向上遍历每一行;2. 遍历当前行所有列;3. 原地更新当前位置数值为自身+下一行两个相邻最小值;4. 遍历结束,顶部元素即为最小路径和。

复杂度:时间 O(n²),n为三角形行数;空间 O(1)

优缺点:极致空间优化,自底向上无边界判断,路径DP经典模板。

易错点总结:自顶向下边界处理繁琐;行列遍历顺序错误;相邻节点选取偏差。

示例测试:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]],输出 11

64. 最小路径和(中等)

题目简述:给定非负整数二维网格,只能向右或向下移动,求从左上角到右下角的最小路径和。

解题思路一:二维网格DP(标准模板)

核心思路:dp[i][j]表示到达(i,j)的最小路径和,当前位置只能从上方或左方到达,状态转移:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j],预处理首行首列边界。

解题步骤:1. 预处理首行:只能从左方移动,累加赋值;2. 预处理首列:只能从上方移动,累加赋值;3. 遍历其余行列,按状态转移公式更新;4. 右下角元素即为答案。

复杂度:时间 O(mn);空间 O(mn)(可原地优化为O(1))

优缺点:网格DP入门万能模板,适配所有二维路径最值问题。

易错点总结:首行首列边界预处理遗漏;移动方向判断错误;状态转移公式颠倒。

63. 不同路径 II(中等)

题目简述:二维网格存在障碍物,无法通过障碍单元格,只能向右向下移动,求从左上角到右下角的路径总数。

解题思路一:障碍适配二维DP(网格进阶模板)

核心思路:继承基础路径DP逻辑,新增障碍判断:当前位置为障碍则路径数置0,无法通行;正常位置仍由上方、左方路径数累加得到。

解题步骤:1. 起点/终点为障碍直接返回0;2. 初始化首行首列,遇到障碍后后续位置全部置0;3. 遍历其余单元格,障碍置0,无障碍则累加左、上路径数;4. 返回右下角路径数。

复杂度:时间 O(mn);空间 O(mn)

优缺点:带约束网格路径模板,适配所有含障碍、含约束的二维路径统计问题。

易错点总结:障碍位置未清零;首行首列连续障碍处理错误;起点终点边界遗漏。

5. 最长回文子串(中等)

题目简述:给定字符串,找出其中最长的回文子串,子串连续不可间断。

解题思路一:中心扩散法(最优实用模板)

核心思路:回文串存在奇数、偶数两种形态,分别以单个字符、两个字符为中心,向左右扩散,判断字符是否相等,记录最长合法子串。

解题步骤:1. 遍历每个字符作为奇数回文中心,双向扩散;2. 遍历每两个相邻字符作为偶数回文中心,双向扩散;3. 每次扩散更新最长回文子串的起始位置与长度;4. 遍历结束截取返回结果。

复杂度:时间 O(n²);空间 O(1)

优缺点:代码简洁、常数小、易手撕,实战优于DP,面试首选。

解题思路二:二维区间DP(理论模板)

核心思路:dp[i][j]表示区间[i,j]是否为回文串,短区间推导长区间:首尾字符相等且内层区间为回文,则当前区间为回文。

复杂度:时间 O(n²);空间 O(n²)

优缺点:区间DP经典应用;空间开销大,实战不推荐。

示例测试:s = "babad",输出 "bab" 或 "aba"

97. 交错字符串(中等)

题目简述:给定三个字符串s1、s2、s3,判断s3是否可由s1和s2的字符交错组成,不改变原字符串字符顺序。

解题思路一:二维序列匹配DP(标准模板)

核心思路:dp[i][j]表示s1前i个字符+s2前j个字符能否构成s3前i+j个字符,当前位匹配s1或s2对应位,且前置状态合法,则当前状态合法。

解题步骤:1. 初始化dp[0][0]=true,空串匹配空串;2. 预处理仅s1、仅s2匹配的边界;3. 遍历二维区间,分别校验匹配s1末尾或s2末尾;4. 最终dp[m][n]即为答案。

复杂度:时间 O(mn);空间 O(mn)

优缺点:双序列交错匹配专属模板,逻辑严谨,覆盖所有匹配场景。

易错点总结:下标对应关系混乱;前置状态校验遗漏;边界初始化不全。

72. 编辑距离(中等)

题目简述:给定两个单词,计算将一个单词转换为另一个单词的最少操作数,支持插入、删除、替换三种操作。

解题思路一:二维字符串DP(经典万能模板)

核心思路:dp[i][j]表示word1前i个字符转为word2前j个字符的最少操作数,字符相等则继承前置状态,不等则取插入、删除、替换的最小值+1。

解题步骤:1. 初始化边界:单串为空时,操作数等于另一串长度;2. 遍历二维所有位置;3. 当前字符相等:dp[i][j] = dp[i-1][j-1];4. 当前字符不等:取三种操作最小值+1;5. 右下角为最终最小编辑距离。

复杂度:时间 O(mn);空间 O(mn)

优缺点:字符串DP天花板模板,适配所有双序列编辑、匹配问题,面试必考。

易错点总结:三种操作状态对应错误;边界初始化颠倒;状态转移公式取值错误。

示例测试:word1 = "horse", word2 = "ros",输出 3

123. 买卖股票的最佳时机 III(困难)

题目简述:给定股票每日价格,最多完成两次交易,求最大利润,不能同时持有多只股票。

解题思路一:四维状态压缩DP(高阶股票模板)

核心思路:拆分四种核心状态:第一次持有、第一次卖出、第二次持有、第二次卖出,每日迭代更新四种状态,最终两次卖出的最大值即为答案。

复杂度:时间 O(n);空间 O(1)状态压缩

优缺点:有限次数股票交易标杆模板,状态清晰,可拓展任意交易次数。

188. 买卖股票的最佳时机 IV(困难)

题目简述:给定最多k次交易次数,求股票交易最大利润,是所有股票问题的通用题型。

解题思路一:二维通用股票DP(万能模板)

核心思路:dp[i][j][0/1]表示第i天完成j次交易、持有/未持有股票的最大利润,统一状态转移规则,适配任意k值交易限制。

解题步骤:1. 初始化交易0次的边界状态;2. 遍历每一天、每一种交易次数;3. 分别更新持有、未持有状态的最大值;4. 遍历结束取所有次数的未持有状态最大值。

复杂度:时间 O(nk);空间 O(nk)(可压缩)

优缺点:股票问题终极通用模板,涵盖所有有限交易次数场景,面试压轴模板。

221. 最大正方形(中等)

题目简述:给定二维二进制矩阵,找出只包含1的最大正方形,返回其面积。

解题思路一:二维网格正方形DP(专属模板)

核心思路:dp[i][j]表示以(i,j)为右下角的最大正方形边长,核心转移公式:当前网格为1时,dp[i][j] = min(左上、上方、左方dp值) + 1,三个方向最小值决定当前正方形上限。

解题步骤:1. 初始化首行首列dp值与网格一致;2. 遍历其余单元格,网格为1则更新边长,否则置0;3. 实时记录最大边长;4. 最终返回边长平方即为面积。

复杂度:时间 O(mn);空间 O(mn)

优缺点:二维正方形最值唯一最优模板,转移公式固定,记忆性极强。

易错点总结:取最小值误写为最大值;0单元格未置0;面积计算遗漏平方。

示例测试:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]],输出 4