:isso-id: /notes/writeups/leetcode/index ================= Leetcode 刷题记录 ================= .. tip:: 用 :file:`./new-problem.sh` 来开始一道新题目: .. dropdown:: new-problem.sh .. literalinclude:: ./new-problem.sh :language: bash .. contents:: 题解 :local: Two Sum ------- .. leetcode:: _ :id: two-sum :diffculty: Easy :language: rust 我居然以为是 a+b 真是太蠢了。 花了一些时间来回忆 rust 的语法,工作后技术直觉好了很多,之前觉得不容易理解的地方 (指 rust)现在觉得非常直观了。 Valid Parentheses ----------------- .. leetcode:: _ :id: valid-parentheses :diffculty: Easy :language: rust :date: 2021-05-04 熟悉语法…… LRU Cache --------- .. leetcode:: _ :id: lru-cache :diffculty: Medium :language: go :date: 2021-05-08 想用 rust 写个 LRU cache 吧发现 `std::collection:LinkedList` 没有暴露出类似 链表节点的结构体……有的话所有权也是问题,`Cursor` 看起来像然而 nightly only, 好像还是太菜了 —— 是说我自己。 但如果换成 go 的话…… :del:`这么简单的题也算 medium 吗` ,可能因为太实用了所以写起来不难? LFU Cache --------- .. leetcode:: _ :id: lfu-cache :diffculty: Medium :language: go :date: 2021-05-08 2021-7-22 2026-03-16 :reference: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Go/LRU_LFU_interview.md 解法 1 在 touch 一个元素的的时候从链表尾部往上找,是一个 :math:`O(n)` 的操作,然而 Leetcode 给过了…… 要是在 `SCAU OJ`_ 是肯定要 TLE。 .. note:: 看一眼输入输出限制,想想边界值,比如 `cap == 0` 的情况就忽略了 解法 2 更好的做法是按 freq 分成多个桶,每次 touch 一个元素就把它挪到对应的 frequency 的桶里,并且 cache 内维护一个 minFreq 方便立刻找到最应该该淘汰的桶, 桶内部是一个小的 LRU,这样 touch 就是 :math:`O(1)` 了。 想过另一个做法是维护一个 freq 为结点值的最小堆,但本质上和方法一没区别,只是把 :math:`O(n)` 的查找变成 :math:`O(\log n)` 而已,大量重复的 freq 值是很浪费时间和空间的。 .. _SCAU OJ: http://acm.scau.edu.cn:8000 Add Two Numbers --------------- .. leetcode:: _ :id: add-two-numbers :diffculty: Medium :language: go :date: 2021-05-13 凡是链表的题目都不能用 rust :'( Partition Equal Subset Sum -------------------------- .. leetcode:: _ :id: partition-equal-subset-sum :diffculty: Medium :language: rust :key: 动态规划 :date: 2021-06-21 2024-04-01 2026-03-22 把一个集合分成两个,使其 sum 分别相等。假设原集合 sum 为 |Sa| ,从集合中选出一个子集,使其 sum 刚好为 |Sa|/2 ,剩下的元素组成的子集的 sum 自然也为 |Sa|/2。因此问题可以转化为一个 :ref:`01-pack` 问题,背包容量为 |Sa|/2,要求恰好装满,填充物的 cost 是数字的值,value 统一设置为 1,因为只需要证有解。 2024-04-01 用 Go 重写了一次,舍弃了空间优化,定义了完整的状态数组 `dp[0..N][0..Sa/2]`,当然原理上没有区别。 需要手动处理化无物可放的情况,即初始化 `dp[0]` 没有区别,要求恰好装满: - 容量为 0 的情况下什么都不放,即 `dp[i][0] = 0` - 没有东西可放时,容量非 0 的情况无解,即 `dp[i][j] = -Inf` where `j != 0` 状态转移方程当然也不变:: 1..N → i 0..Sa/2 → j Ci 是第 i 件物品的 cost if j-Ci >= 0: dp[i][j] = max(dp[i-1][j], dp[i-1][j-Ci]+1) else: dp[i][j] = dp[i-1][j] 但注意:*需要手动处理* `j-Ci<0` *的情况*:此时放不下第 i 件物品,只能不放了 `dp[i-1][j]` TODO: 显示多种语言的解法 2026-03-22 GPT 给了一种 Bool DP 的做法,状态定义是: dp[i][j]:前 i + 1 个数里,是否能选出若干个数,使和恰好等于 j .. |Sa| replace:: S\ :sub:`a` Merge Two Sorted Lists ---------------------- .. leetcode:: _ :id: merge-two-sorted-lists :diffculty: Easy :language: go :date: 2021-07-05 :key: 链表 纯逻辑题。 Maximum Subarray ---------------- .. leetcode:: _ :id: maximum-subarray :diffculty: Easy :language: go :key: 动态规划 分治法 :date: 2021-07-05 2021-07-06 题目本身比较简单,一维 DP 或者贪心 :math:`O(n)` 可做。 .. |Ml| replace:: M\ :sub:`left` .. |Mr| replace:: M\ :sub:`right` .. |Mm| replace:: M\ :sub:`middle` 题干提示可用分治法做,"which is more subtle",是一个吃力不讨好的解法。但很有代表性: 设数组最大子序列为 M ,M = max(|Ml|, |Mr|, |Mm|),分别为左半边数组的最大子序列,右半边数组的最大子序列,或者是从中间算起,横跨左右的最大子序列。 - 当问题规模缩减至 1 的时候, |Ml|, |Mr|, |Mm| 显然为数组里唯一的元素 - |Mm| 的值不可由子问题推导出来,只能在数组 l 和 r 分别逆序和顺序遍历,求各自的从边缘开始的最大子序列,是一个 :math:`O(n)` 的操作 -- 这就决定了这个解法比 DP 慢,在每一轮子问题的解决都要遍历一次 - 二分法,所以问题要 :math:`O(\log_{2}n)` 个规模的子问题 复杂度为 :math:`O(n\log n)` 。 .. seealso:: `算法复杂度中的O(logN)底数是多少`_ .. _算法复杂度中的O(logN)底数是多少: https://www.cnblogs.com/lulin1/p/9516132.html Climbing Stairs --------------- .. leetcode:: _ :id: climbing-stairs :diffculty: Easy :language: go :key: 搜索 动态规划 :date: 2021-07-06 :reference: https://blog.csdn.net/zgpeace/article/details/88382121 记忆化搜索 写一个暴力版本,时间复杂度 :math:`O(2^n)`。记忆冗余结果后复杂度应为 :math:`O(n)`(?)。空间复杂度 :math:`O(n)` 递推 有一点动态规划的味道,但逻辑上非常简单,时间空间复杂度都是 :math:`O(n)` 斐波那契数列 空间上当然可以到 :math:`O(1)` ,不写了 Binary Tree Inorder Traversal ----------------------------- .. leetcode:: _ :id: binary-tree-inorder-traversal :diffculty: Easy :language: go :key: 二叉树 :date: 2021-07-06 纯数据结构题,没啥好说。 Symmetric Tree -------------- .. leetcode:: _ :id: symmetric-tree :diffculty: Easy :language: go :key: 二叉树 :date: 2021-07-06 :reference: https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/er-cha-shu-de-zhong-xu-bian-li-by-leetcode-solutio/ 递归解法 按递归做的话是带点变化的数据结构题,左右子树互为镜像,任意对称的节点的左子树等于右子树。 非递归解法 引入栈,按 `左->中->右` 和 `右->中->左` 应得到完全相同的序列。 .. tip:: 前序遍历写起来应当简单一点 Maximum Depth Of Binary Tree ---------------------------- .. leetcode:: _ :id: maximum-depth-of-binary-tree :diffculty: Easy :language: go :key: 二叉树 :date: 2021-07-07 数据结构题。 Best Time to Buy and Sell Stock ------------------------------- .. leetcode:: _ :id: best-time-to-buy-and-sell-stock :diffculty: Easy :language: rust :key: 数组 :date: 2021-07-07 2026-03-29 :reference: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/bao-li-mei-ju-dong-tai-gui-hua-chai-fen-si-xiang-b/ 写了三个版本: 暴力 TLE,每次 `i+1..prices.len()` 的回溯有大量荣誉计算,复杂度为 :math:`O(n!)` 暴力 + 稍微剪枝 内层循环的 `(0..i).rev()` 很重要,确保新出现的低价被计算到。 复杂度依然为 :math:`O(n!)` ,只是有几率避免冗余计算,勉强 AC 但时间上只超过了 8% 的选手,有问题。 .. note:: 实际上 `profit[i]` 可以只从 `profit[i-1]` 推断,见下 DP 更好的状态转移方程是 `profit[i] = max(profit[i-1] + (prices[i] - prices[i-1]), 0)` 。复杂度为 :math:`O(n)` ,超过了 85%+ 的选手,够了。 从题意上看,方程的意思是:在第 i-1 天我们已经取得了能取得的最大收益,那第 i 天也应该参考第 i-1 天的购入时机,如果亏本了,则不购入。 2026-03-29: 看起来不太对。 记录低价 只买卖一次,所以记录下 maxProfit 和 lowPrice,每次只看 max(maxProfit, price[i] - lowPrice) 即可,遇到 `price[i] < lowPrice` 要及时更新。 Invert Binary Tree ------------------ .. leetcode:: _ :id: invert-binary-tree :diffculty: Easy :language: go :key: 二叉树 :date: 2021-07-07 2026-03-23 我能 `去 Google`__ 了吗? __ https://twitter.com/mxcl/status/608682016205344768 Best Time to Buy and Sell Stock II ---------------------------------- .. leetcode:: _ :id: best-time-to-buy-and-sell-stock-ii :diffculty: Medium :language: rust :key: 贪心 :date: 2021-07-07 2026-03-29 :leetcode:`Best Time to Buy and Sell Stock` 的一个简单变体,允许多次买卖,贪心即可,每次有价差则卖出。 Best Time to Buy and Sell Stock III ----------------------------------- .. leetcode:: _ :id: best-time-to-buy-and-sell-stock-iii :diffculty: Hard :language: rust :key: 动态规划 :date: 2021-07-07 2021-07-28 :reference: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/solution/mai-mai-gu-piao-de-zui-jia-shi-ji-iii-by-wrnt/ :leetcode:`Best Time to Buy and Sell Stock` 的一个更难的变体,允许至多两次不重叠的买卖。 解法一 按 :leetcode:`Best Time to Buy and Sell Stock` 的解法,进行一次买卖,在此基础得到一个第 i 天收入的数组 `max_profit1` ,*该收入不一定是当日卖出所得* 第二次买卖,需在第一次买卖后一天(当天卖,当天买没有意义,收益和一次买卖没差):: profit2[i] = max(max_profit1[i-2], profit2[i-1]) + (prices[i] - prices[i-1]) `profit2` 数组为第一次买卖 *后* ,第 i 天的收入的数组,若收入为负,则放弃该交易,收入为 0。 可以看到 `profit2[i]` 肯定会赚 i-1 天的差价 `prices[i] - prices[i-1]`,但可以选择加上 i-2 天时第一次买卖的最大收入 `max_profit1[i-2]` 或者沿用 i-1 t天做第二次买卖的最优策略。 最终 `profit2` 数组中的最大值为答案。 复杂度为 :math:`2*O(n)` ,这个常数可以优化掉,测评里只比 6.67% 的选手快,:math:`O(n)` 已经是这个问题的极限了,暂时不知道哪里有问题。 解法2 看的题解。 对每个状态(两次购买,两次出售)各构造一个互相依赖的状态转移方程。 Single Number ------------- .. leetcode:: _ :id: single-number :diffculty: Easy :language: rust :key: 位操作 :date: 2021-07-07 :reference: https://www.cnblogs.com/grandyang/p/4130577.html 遥想 :people:`pcf` 师傅还跟我讨论过这题,可惜没记住。反正不看题解打死也做不出来。 Diameter Of Binary Tree ----------------------- .. leetcode:: _ :id: diameter-of-binary-tree :diffculty: Easy :language: go :key: 二叉树 :date: 2021-07-08 :reference: https://www.cnblogs.com/wangxiaoyong/p/10449634.html 这题本不难,答案是所有节点中「左子树深度 + 右子树深度」最大的值。 解法1 没能 AC,留下是为了提醒自己。 实现稍复杂,思路上是实现一个对每个节点返回左右臂展(其实就是深度)的函数:需要考虑 `root.Left != nil` 和 `root.Right != nil` 的情况,总之是对的,但因为思路的不明确,实现了一个 `func maxInts(s ...int)` 的函数,在递归前存了 `res` 在数组里,在递归后拿它来做运算…… 非常典型的错误 解法2 仅修正了比较前的 `res` 被覆盖的问题,AC ,但 `maxInts` 很慢。 解法3 标准解法,参考里的题解有个莫名其妙的 `+1` 再 `-1` ,没有用。 Merge Two Binary Trees ---------------------- .. leetcode:: _ :id: merge-two-binary-trees :diffculty: Easy :language: go :key: 二叉树 :date: 2021-07-09 数据结构题。 Maximum Product Subarray ------------------------ .. leetcode:: _ :id: maximum-product-subarray :diffculty: Medium :language: rust :key: 动态规划 :date: 2021-07-09 :reference: https://leetcode-cn.com/problems/maximum-product-subarray/solution/cheng-ji-zui-da-zi-shu-zu-by-leetcode-solution/ :leetcode:`Maximum Subarray` 的变体,求乘积最大的子序列。偷偷看了一眼题解:得到了「开两个 dp 数组」的提示。 `N` 为给定数组,用 `P[i]` 表示以 i 结尾的子序列的最大乘积,假设数组只有非负数,那么 `P[i]` 的值只和 `N[i]` 和 `P[i-1]` 相关: `P[i] = P[i-1] * N[i]` 。 但数组可能出现负数: - 用 `P[i]` 表示以 i 结尾的子序列的最大正乘积 - 用 `Pn[i]` 表示以 i 结尾的子序列的最小负乘积 根据 `N[i]` 的正负不同:`Pn` 的值可能转化为 `P`,`P` 的值可能也转化为 `Pn`: - `P[i] = max(N[0], P[i-1]*N[0], Pn[i-1]*N[0])` - `Pn[i] = min(N[0], P[i-1]*N[0], Pn[i-1]*N[0])` Shortest Subarray To Be Removed To Make Array Sorted ---------------------------------------------------- .. leetcode:: _ :id: shortest-subarray-to-be-removed-to-make-array-sorted :diffculty: Medium :language: rust :date: 2021-07-09 略难,写了很复杂的代码依然 WA,感受是:当你需要判断非常复杂的情况时,思路大概率部队。 移除 *一个* 最短的子序列使整个数组有序,那该数组必形如:`[ 有序..., 无序..., 有序...]`,当然两个有序的部分可能是空数组。数组为 `N`,易从左到右分别求出有序的部分 `[0,l]` 和 `[r, len(N)-1]`,那 `[l+1, r-1]` 是否就为最小的无序子序列呢? 非也,`[0,l]` 和 `[r, len(N)-1]` 分别有序,但整体不一定有序,而且可能重叠,如 `[1, 2, 100]` 和 `[0, 2, 5]`,从 `ll in l->0` 和 `rr in len(N)-1 -> r` 两个方向找恰好 `N[ll] < N[rr]` 即为答案,递归可做。 .. note:: 注意整个数组有序的边界条件。 House Robber ------------ .. leetcode:: _ :id: house-robber :diffculty: Medium :language: rust :key: 动态规划 :date: 2021-07-09 抢劫第 i 间房子能获得财产 `M[i]`,最大收入 `R[i]`。递推方程:`R[i] = max(R[i-2], R[i-2]) + M[i]`,答案为最大的 `R[i]`。 手动初始化前三个 R 有点累。 Longest Increasing Subsequence ------------------------------ .. leetcode:: _ :id: longest-increasing-subsequence :diffculty: Medium :language: rust :key: 动态规划 二分法 :date: 2021-07-12 :reference: https://blog.csdn.net/lxt_Lucia/article/details/81206439 经典 DP 题。 维护以 `i` 结尾的 LIS 的长度 设数组为 `N`,`F[i]` 为以 `i` 结尾的最长上升子序列的长度,有递推式:`F[i] = F[j]+1`,where `N[i] < N[j]`,这个 j 得通过一个 `0..i` 的循环获取,因此复杂度 为 :math:`O(n^2)` 维护长度为 `i` 的 LIS 结尾元素的最小值 复杂度 :math:`O(n\log n)`,是我想不出来的解法 T_T。 .. note:: 感觉没有说明白,算了。 Edit Distance ------------- .. leetcode:: _ :id: edit-distance :diffculty: Hard :language: rust :key: 动态规划 :date: 2021-07-12 :reference: https://leetcode-cn.com/problems/edit-distance/solution/bian-ji-ju-chi-by-leetcode-solution/ 太难了……毫无思路直接看题解。 Minimum ASCII Delete Sum for Two Strings ---------------------------------------- .. leetcode:: _ :id: minimum-ascii-delete-sum-for-two-strings :diffculty: Medium :language: rust :key: 动态规划 :date: 2021-07-12 :leetcode:`Edit Distance` 的变种,将最少操作数变成了最少的 ASCII 之和而已。 一开始审错题,难受。 Longest Common Subsequence -------------------------- .. leetcode:: _ :id: longest-common-subsequence :diffculty: Medium :language: rust :key: 动态规划 :date: 2021-07-13 涉及两个数组的 DP 问题常常是二维 DP,和 `:leetcode:`Edit Distance` 的思路有相似之处。 两串为 `S1`, `S2`。`定义数组 `D[i][j]` 表示 `S1[0..i]` `S2[0..j]` 的 LCS 长度: - 讨论 `D[i-1][j]`, `D[i][j-1]`, `D[i-1][j-1]` 和 `D[i][j]` 的递推关系 - 讨论可能的 `D[0..i][j]`, `D[i][0..j]` 的初始化 .. note:: 当 `S1[0..i] = A B C D` `S2[0..j] = A D`,不需要在 `D[i-1][j]` 中讨论 `S1[i-1] == D` 的加入对 LCS 长度的影响,这部分情况完全由 `D[i-1][j-1]` 覆盖。 因此递推公式为:: D[i][j] = max(D[i-1][j], D[i][j-1], D[i-1][j-1] + X) where X = 1 when S1[i-1] == S2[j-1] 当 `S1[i-1] == S2[j-1]` 时,LCS 延长。 Longest Palindromic Subsequence ------------------------------- .. leetcode:: _ :id: longest-palindromic-subsequence :diffculty: Medium :language: rust :key: 动态规划 :date: 2021-07-13 最长回文串。 作为 :leetcode:`Longest Common Subsequence` 的变种 将字符串翻转过来作为第二个数组,求 LCS 即可。 .. todo:: 常规解法 Longest Palindromic Substring ----------------------------- .. leetcode:: _ :id: longest-palindromic-substring :diffculty: Medium :language: rust :key: 递归 动态规划 :date: 2021-07-13 钻了牛角尖……还不如直接看题解。 分情况递归 字符串为 `S`,开一个全局变量存最大回文字串的区间 `ANS = (0, 0)`,对每一个 `S[i]`,从中间往两边扫,可获得所有的 "YXY" 的奇数回文串。但注意有 "YXXY" 的偶数回文串,则对每一个相等的 `S[i-1]` 和 `S[i]` 往两边扫。 复杂度为 :math:`O(n^2)`,感觉可以用记忆化优化一下。 DP 状态数组 `D[i][j]` 表示 `S[i..j]` 是否为回文串。若 `S[i] == S[j]`,则 `D[i][j]` 为回文串的话需要: - `i - j < 2` - 或者 `D[i+1][j-1]` 为回文串 where `i - j < 2` 复杂度同为 :math:`O(n^2)`。 .. note:: 应当注意两层循环的方向,外层 `i = n -> 0`,内层 `j = i -> n` 是为了保证求 `D[i][j]` 时 `D[i+1][j-1]` 已解出 .. todo:: 听说有 :math:`O(n)` 的做法,改日再学习吧。 Linked List Cycle ----------------- .. leetcode:: _ :id: linked-list-cycle :diffculty: Easy :language: go :key: 链表 快慢指针 :date: 2021-07-13 2026-03-22 无论如何时间复杂度都是 :math:`O(n)`,用哈希标表存 visited 的做法不用说了。 题目要求用 :math:`O(1)` 空间,估计我独立做不出来。很久前听 :people:`pcf` 说到用两个指针,所以稍微回忆了一下:用两个步长不一致的指针,一个每次一个节点,一个每次两个节点,如果成环的话总会相遇。 .. seealso:: :people:`lifeiren` 的 解法_ 惊为天人 .. _解法: https://leetcode-cn.com/problems/linked-list-cycle/solution/qiao-miao-li-yong-zhi-zhen-cun-chu-jie-d-xeca/ Linked List Cycle II -------------------- .. leetcode:: _ :id: linked-list-cycle-ii :diffculty: Medium :language: go :key: 链表 快慢指针 :date: 2021-07-15 2026-03-22 :reference: https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/huan-xing-lian-biao-ii-by-leetcode-solution/ 看的题解。 我根本没在动脑子…… :( 2026-03-22 题解假设 fast 走过的距离为 a+n*(b+c)+b,而 slow 走过的距离为 a+b,隐含了一个条件: slow 和 fast 相遇时 slow 还没走完一圈。 条件是成立的,假设环长是 L。 当 slow 刚进入环时 fast 已在环内任意位置。 fast 每轮比 slow 多走 1 步,也就是以 slow 为静止的参照系,无论 fast 在哪里, 只要走 L-1 次(L-1*2 步)fast 就能踏遍环里的每个地方,自然能和 slow 重合过(不会跳过,可以自行模拟一下), 此时 slow 也至多走了 L-1 步,不足一环。 另外假设快慢指针的比例不是 1:2,导致 fast 可能越过 slow,让条件不成立,依然不影响结论。 假设 slow 走过 m 圈,走过的距离为 a+b+m*(b+c),带入算算的结果应该是一样的,详情问 GPT。 Product of Array Except Self ---------------------------- .. leetcode:: _ :id: product-of-array-except-self :diffculty: Medium :language: :key: 前缀数组 :date: 2021-07-15 :reference: https://cntofu.com/book/186/problems/238.product-of-array-except-self.md 不许用除法,想不出来,看的题解。 双前缀数组 巧妙的双前缀数组,时间空间复杂度均为 :math:`O(n)`。 双前缀数组 无无额外空间 题目希望 :math:`O(1)` 的空间复杂度,可以用一个临时变量存累乘结果,直接用存答案的数组的空间。 Trapping Rain Water ------------------- .. leetcode:: _ :id: trapping-rain-water :diffculty: Hard :language: rust :key: 动态规划 双指针 单调栈 :date: 2021-07-15 似乎 :people:`pcf` 也和我提到过,然而完全忘了。 看题解。 动态规划 其实也不像动态规划……倒不如说是 :leetcode:`Product of Array Except Self` 的变种,同样用到两个数组。 `L[i]`, `R[i]` 为以第 `i` 格为中心,左右的最高点的高度,每一格可能容纳的水量为 `W[i] = min(L[i], R[i]) - Height[i]`。 `单调栈 `_ 利用单调栈的性质,维护一个由高到低的水洼左边,每次 pop 的时候,算该水洼里的一层水 .. todo:: 还有 :math:`O(1)` 解法…… 歇会儿。 Next Greater Element I ---------------------- .. leetcode:: _ :id: next-greater-element-i :diffculty: Easy :language: rust :key: 单调栈 :date: 2021-07-16 读题花了挺久…… 暴力法可直接做,复杂度 :math:`O(m*n),`m` 为 `nums1` 长度,`n` 为 `nums2` 长度。 更好的做法是对 `nums2` 维护一个数组 `G[i]`,代表在 `nums2[i]` 右边比它大的元素(即 Next Greater Element),将 `nums1[i] => i` 的映射存在哈希表中,遍历 `nums2` 时可以得出答案。 `G[i]` 的求法为:从 `nums2.len() => 0` 方向维护一个单调递减的栈,依次尝试 push `nums2[i]`,当比栈顶大时,将栈中已有元素 pop 出;当比栈顶小时,`G[i] = top_of_stack`,之后 `nums2[i]` 入栈。 .. tip:: `G[i]` 不依赖上一次循环的结果,在实际中可以就地求出,不必开辟空间 建哈希表复杂度为 :math:`O(m)`,建单调栈复杂度为 :math:`O(n)`,总的时间复杂度为 :math:`O(m+n)`。 Swap Nodes in Pairs ------------------- .. leetcode:: _ :id: swap-nodes-in-pairs :diffculty: Medium :language: go :key: 链表 :date: 2021-07-16 用一个步进为 2 的循环即可。 Reverse Linked List ------------------- .. leetcode:: _ :id: reverse-linked-list :diffculty: Easy :language: go :key: 链表 :date: 2021-07-16 2026-03-23 :reference: https://zhuanlan.zhihu.com/p/86745433 :del:`没啥好说`。 递归 万万没想到……递归我没写出来。看题解,题解说很明白了。 2026-03-23: 写出来了 迭代 拿个栈。 2026-03-23: 存 cur 和 next 两个指针。 Reverse Linked List II ---------------------- .. leetcode:: _ :id: reverse-linked-list-ii :diffculty: Medium :language: go :key: 链表 :date: 2021-07-16 :leetcode:`Reverse Linked List` 的变种。被翻转的链表的 tail 应始终指向右边不翻转的部分,因此处理右边界的时候要花点心思。 Implement Trie Prefix Tree -------------------------- .. leetcode:: _ :id: implement-trie-prefix-tree :diffculty: Medium :language: go :key: Tire树 :date: 2021-07-16 纯数据结构题。 Combination Sum --------------- .. leetcode:: _ :id: combination-sum :diffculty: Medium :language: rust :key: 回溯 :date: 2021-07-17 从给定的 `candicates` 生成 sum 为 `target` 的组合。 递归可破,和之前的题不一样的是在每一次调用都伴随着一个新的解,所以要带着解的数组一起递归。 Generate Parentheses -------------------- .. leetcode:: _ :id: generate-parentheses :diffculty: Medium :language: rust :key: 回溯 :date: 2021-07-17 和 :leetcode:`Combination Sum` 类似的解法。 每一次递归中需要的决策是:要闭合几个未闭合的括号。 Sort an Array ------------- .. leetcode:: _ :id: sort-an-array :diffculty: Medium :language: rust :key: 排序 :date: 2021-07-19 2026-03-16 :reference: https://rust-algo.club/sorting/quicksort 2021-07-17 情绪又不好了,看了近两个小时的快排教程没看进去。 pivot 固定为 ``nums[len-1]`` 会 TLE,因为有一个近乎有序的大数组 case, 导致 Partition 不均衡(数据倾斜):分为 ``nums[0..len-2]`` 和 ``nums[nums-1..]`` Rust 标准库没有生成随机数的函数……糊了一个。 2026-03-16 重新写了一遍,我比以前聪明诶。 Combination Sum II ------------------ .. leetcode:: _ :id: combination-sum-ii :diffculty: Medium :language: rust :key: 回溯 :date: 2021-07-19 :leetcode:`Combination Sum` 的变种。 增加了 `candicates` 可重复、以及结果不许重复的两个限制。 3Sum ---- .. leetcode:: _ :id: 3sum :diffculty: Medium :language: rust :key: 双指针 :date: 2021-07-19 :reference: https://leetcode-cn.com/problems/3sum/solution/san-shu-zhi-he-by-leetcode-solution/ 题目有双指针的标签,我怎么觉得回溯法就能做?试试看。 不能,评测 TLE 了,本地则是爆栈 双指针 看题解,把三重循环优化到二重了,复杂度 :math:`O(n^2)` .. note:: 答案不许重复,`i`,`j` 的循环里都有 `nums[i] == nums[i - 1]` 的判断以跳过重复元素,但不必要对 `k` 判断,因为 `i`,`j` 已经不重复了,`k` 自然不重复 Reverse Nodes In K Group ------------------------ .. leetcode:: _ :id: reverse-nodes-in-k-group :diffculty: Hard :language: go :key: 链表 :date: 2021-07-19 :leetcode:`Reverse Linked List II` 的变种,多了两个返回值: 一个用于返回翻转后的链表 tail,方便接下一段翻转链表,另一个一个用于判断该段需不需要 reverse,比较琐碎。 Evaluate Reverse Polish Notation -------------------------------- .. leetcode:: _ :id: evaluate-reverse-polish-notation :diffculty: Medium :language: rust :date: 2021-07-20 模拟题,只是对 rust 的字符串操作不太熟悉。 Balanced Binary Tree -------------------- .. leetcode:: _ :id: balanced-binary-tree :diffculty: Easy :language: go :key: 二叉树 平衡二叉树 :date: 2021-07-20 判断子树是否平衡的时候要同时返回子树深度供父节点用。 Convert Sorted Array To Binary Search Tree ------------------------------------------ .. leetcode:: _ :id: convert-sorted-array-to-binary-search-tree :diffculty: Easy :language: go :key: 二叉树 二叉搜索树 :date: 2021-07-20 数组本身排好序了,用二分的方法就能找到每一层的 root。 Balance A Binary Search Tree ---------------------------- .. leetcode:: _ :id: balance-a-binary-search-tree :diffculty: Medium :language: go :key: 二叉树 平衡二叉树 :date: 2021-07-20 :leetcode:`Convert Sorted Array To Binary Search Tree` 的变种。 中序遍历 BST 能得到一个有序数组,用二分法构造出来的 BST 就是平衡的。 Ones and Zeroes --------------- .. leetcode:: _ :id: ones-and-zeroes :diffculty: Medium :language: go :key: 动态规划 :date: 2021-07-28 01 背包问题,但需要同时填满两个容量不同的背包。 .. note:: 在不优化的情况下,空间复杂度为 :math:`O(len*m*n)`,需要一个三维的状态数组, 在 `m < count0 && n < count1` 的情况下,需要将 `dp[i-1]` 的状态复制到 `dp[i]` 反之,可以应用递推公式。 在优化的空间的情况下,第 i 轮循环中,未遍历 dp 的值即为 i-1 轮循环残留的值。 -------------------------------------------------------------------------------- .. rubric:: 脚注 Merge Sorted Array ------------------ .. leetcode:: _ :id: merge-sorted-array :diffculty: Easy :language: go :date: 2026-03-05 很简单的一道题,要不先把 nums1 挪开然后直接合并,要不逆序。之前想复杂了。 Remove Element -------------- .. leetcode:: _ :id: remove-element :diffculty: Easy :language: go :key: 数组 :date: 2026-03-27 Remove Duplicates From Sorted Array ----------------------------------- .. leetcode:: _ :id: remove-duplicates-from-sorted-array :diffculty: Easy :language: go :key: 数组 :date: 2026-03-27 Remove Duplicates From Sorted Array II -------------------------------------- .. leetcode:: _ :id: remove-duplicates-from-sorted-array-ii :diffculty: Medium :language: go :key: 数组 :date: 2026-03-27 Majority Element ---------------- .. leetcode:: _ :id: majority-element :diffculty: Easy :language: go :key: 数组 :date: 2026-03-27 Boyer-Moore 算法。 原理是:每次遇到两个不同的元素,就把它们“配对删除”。 最坏的情况是每个 Majority Element 都和其他的元素配对了被删除,但也会剩下一个。 Rotate Array ------------ .. leetcode:: _ :id: rotate-array :diffculty: Medium :language: go :key: 数组 :date: 2026-03-27 i 位置的元素往右挪动 k 个位置,被修改的下标序列会组成一个环: `[i, (i+k)%n (i+2k)%n, ... i]`。存在 `(i+x*k)%n == i`,即 `(x*k)%n == 0`。 .. todo:: 环的数目为 `gcd(n, k)` Jump Game --------- .. leetcode:: _ :id: jump-game :diffculty: Easy :language: go :key: 贪心 :date: 2026-03-29 Jump Game II ------------ .. leetcode:: _ :id: jump-game-ii :diffculty: Medium :language: go :key: 贪心 :date: 2026-03-29 `解法 1 `_ 非常好懂,`for i in (0..n)` 找出能越过终点(`i+nums[i]>=n-1`)的地方,那里就是最小步数再 + 1 就能达到终点的地方。时间复杂度稍高 解法 2 其实和 1 一样,但利用 `farest` 和 `end` 两个变量配合消除了内部循环,保证 `O(n)` Minimum Absolute Difference In Bst ---------------------------------- .. leetcode:: _ :id: minimum-absolute-difference-in-bst :diffculty: Easy :language: go :key: 二叉搜索树 :date: 2026-03-30 转化为:有序序列里相邻的哪两个数间隔最小。然后中序遍历。 Kth Smallest Element In A Bst ----------------------------- .. leetcode:: _ :id: kth-smallest-element-in-a-bst :diffculty: Medium :language: go :key: 二叉搜索树 :date: 2026-03-30 Validate Binary Search Tree --------------------------- .. leetcode:: _ :id: validate-binary-search-tree :diffculty: Medium :language: go :key: 二叉搜索树 :date: 2026-03-30 Search Insert Position ---------------------- .. leetcode:: _ :id: search-insert-position :diffculty: Easy :language: go :key: 二分查找 :date: 2026-03-30 Search A 2D Matrix ------------------ .. leetcode:: _ :id: search-a-2d-matrix :diffculty: Medium :language: go :key: 二分查找 :date: 2026-03-30