楼主: Myron2017
跳转到指定楼层
上一主题 下一主题
收起左侧

刷题记录帖子

🔗
 楼主| Myron2017 2026-2-9 03:50:21 | 只看该作者
全局:
LC 3834. Merge Adjacent Equal Elements

我挺喜欢这道题目的,考差了用 stack 来模拟 merge 的过程。考察目的明确清晰,代码简单,但是思考过程,考察了用数据结构解决实际问题的能力。

需要注意,stack pop 出来,再插入的时候,需要继续判断是不是会有可以 merge 的情况。
  1. class Solution:
  2.     def mergeAdjacent(self, nums: List[int]) -> List[int]:
  3.         # using stack
  4.         stack = []

  5.         for num in nums:

  6.             if not stack or stack[-1] != num:
  7.                 stack.append(num)
  8.             else: # stack[-1] == num
  9.                 curr = num
  10.                 while stack and stack[-1] == curr:
  11.                     stack.pop()
  12.                     curr *= 2
  13.                 stack.append(curr)

  14.         return stack
  15.         
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-9 10:56:59 | 只看该作者
全局:
LC 239. Sliding Window Maximum

如何在滑动窗口中,高效维护 min 和 max

下面我把它讲成一个可以直接背下来、面试时自动触发的“肌肉记忆”版本。

一、为什么普通办法不行?

在滑动窗口里:

窗口每次右移一格

min / max 会 被加入、被删除

如果你每次都 min(window) / max(window)

👉 时间复杂度是 O(n²)(直接 TLE)

所以我们要的是:

插入 O(1),删除 O(1),查询 min / max O(1)

二、答案:单调队列(Monotonic Deque)
核心思想一句话

队列里只保留“将来可能成为 min / max 的元素”

无用的,提前踢掉。

三、维护 max:单调递减队列
规则

队列里:从大到小

队首:当前窗口的 最大值

新元素进来时:

把 比它小的 全部从队尾删掉(它们永远没机会当 max 了)

代码模板
  1. while maxdq and nums[maxdq[-1]] <= nums[r]:
  2.     maxdq.pop()
  3. maxdq.append(r)
复制代码
四、维护 min:单调递增队列
规则

队列里:从小到大

队首:当前窗口的 最小值

新元素进来时:

把 比它大的 全部从队尾删掉

代码模板
  1. while mindq and nums[mindq[-1]] >= nums[r]:
  2.     mindq.pop()
  3. mindq.append(r)
复制代码
五、窗口左端移动时,如何删除?

关键点:队列里存的是 index,不是值

当 l 右移:
  1. if maxdq[0] == l:
  2.     maxdq.popleft()
  3. if mindq[0] == l:
  4.     mindq.popleft()
复制代码
👉 保证队首永远在窗口内

六、为什么这个方法是 O(n)?

摊还分析(面试加分点):

每个元素:

进队 一次

出队 最多一次

不存在反复扫描

👉 总操作 ≤ 2n

七、用一句话记住单调队列

队列里不是当前窗口的所有元素,而是“候选最值”

先清理过期元素,再取答案(模板顺序)

推荐顺序永远是:

push 新元素

pop 过期元素

取答案

这样你不会出现:

队首已经出窗口,但你还拿它当 max 的情况
  1. from collections import deque

  2. class Solution:
  3.    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
  4.        maxdq = deque()   # store indices, values are decreasing
  5.        res = []

  6.        for r in range(len(nums)):
  7.            # 1. maintain decreasing deque
  8.            while maxdq and nums[r] >= nums[maxdq[-1]]:
  9.                maxdq.pop()
  10.            maxdq.append(r)

  11.            # 2. remove out-of-window index
  12.            if maxdq[0] <= r - k:
  13.                maxdq.popleft()

  14.            # 3. record answer when window is ready
  15.            if r >= k - 1:
  16.                res.append(nums[maxdq[0]])

  17.        return res
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-9 11:24:01 | 只看该作者
全局:
LC 1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit
  1. from collections import deque

  2. class Solution:
  3.     def longestSubarray(self, nums: List[int], limit: int) -> int:
  4.         """
  5.         【整体思路】
  6.         使用滑动窗口 + 单调队列,维护当前窗口内的最大值和最小值。

  7.         核心不变量:
  8.         - maxdq:单调递减队列,队头始终是当前窗口的「最大值索引」
  9.         - mindq:单调递增队列,队头始终是当前窗口的「最小值索引」

  10.         注意:
  11.         - 队列里存的是【索引 index】,不是值
  12.           因为:
  13.           1️⃣ 我们要判断某个元素是否已经移出窗口(index < left)
  14.           2️⃣ 通过 nums[index] 才能拿到真实值

  15.         滑动规则:
  16.         - right 每次右移,加入新元素
  17.         - 若 max - min > limit,说明窗口非法,必须不断移动 left 缩小窗口
  18.         - 只有在窗口合法时,才更新答案
  19.         """

  20.         maxdq = deque()   # 存 index,对应的 nums[index] 单调递减
  21.         mindq = deque()   # 存 index,对应的 nums[index] 单调递增

  22.         left = 0
  23.         ans = 0

  24.         for right in range(len(nums)):
  25.             # ===============================
  26.             # 1. 将 nums[right] 加入 maxdq
  27.             #    保证队列从大到小(队头最大)
  28.             # ===============================
  29.             while maxdq and nums[maxdq[-1]] < nums[right]:
  30.                 maxdq.pop()
  31.             maxdq.append(right)

  32.             # ===============================
  33.             # 2. 将 nums[right] 加入 mindq
  34.             #    保证队列从小到大(队头最小)
  35.             # ===============================
  36.             while mindq and nums[mindq[-1]] > nums[right]:
  37.                 mindq.pop()
  38.             mindq.append(right)

  39.             # ===============================
  40.             # 3. 如果当前窗口不合法:
  41.             #    max - min > limit
  42.             #    就不断移动 left 缩小窗口
  43.             # ===============================
  44.             while nums[maxdq[0]] - nums[mindq[0]] > limit:
  45.                 # 如果窗口左端元素正好是最大值 / 最小值
  46.                 # 需要把对应的 index 从队列中弹出
  47.                 if maxdq[0] == left:
  48.                     maxdq.popleft()
  49.                 if mindq[0] == left:
  50.                     mindq.popleft()
  51.                 left += 1

  52.             # ===============================
  53.             # 4. 明确判断窗口合法后,再更新答案
  54.             # ===============================
  55.             if nums[maxdq[0]] - nums[mindq[0]] <= limit:
  56.                 ans = max(ans, right - left + 1)

  57.         return ans
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-10 05:28:16 | 只看该作者
全局:
LC 1382. Balance a Binary Search Tree

基础题,但是还是很好的练习题,不是那种故意出来刁难你的题。

就是考察你的基本功和刷题细节把握能力。

BST 的中序遍历 = 有序数组

用有序数组建平衡 BST:每次取中点

分治函数的参数 l, r 就是“你能用的数据范围”,绝不能跳出

🔑 一句“防 bug 心法”(这题的精髓)

递归参数定义了边界,
递归调用必须严格在这个边界内。
  1. # Definition for a binary tree node.
  2. # class TreeNode:
  3. #     def __init__(self, val=0, left=None, right=None):
  4. #         self.val = val
  5. #         self.left = left
  6. #         self.right = right

  7. class Solution:
  8.     def balanceBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
  9.         # 用来存储 BST 中序遍历得到的「有序节点值」
  10.         vals_list = []

  11.         def inOrder(node):
  12.             """
  13.             中序遍历(Left -> Root -> Right)
  14.             对 BST 来说,中序遍历结果一定是升序数组
  15.             """
  16.             if not node:
  17.                 return

  18.             inOrder(node.left)          # 访问左子树
  19.             vals_list.append(node.val)  # 当前节点值加入数组
  20.             inOrder(node.right)         # 访问右子树
  21.         
  22.         # Step 1: 将 BST 转换成有序数组
  23.         inOrder(root)

  24.         def buildBlancedBST(l, r):
  25.             """
  26.             使用 vals_list[l : r+1] 这一段有序数组,
  27.             构建一棵「高度平衡 BST」

  28.             l, r 表示当前递归函数可以使用的数组边界
  29.             """
  30.             # 递归终止条件:区间为空
  31.             if l > r:
  32.                 return None

  33.             # 选择中点作为当前子树的根节点
  34.             # 这样可以保证左右子树节点数量尽量相等
  35.             mid = (l + r) // 2

  36.             # 创建当前根节点
  37.             node = TreeNode(val=vals_list[mid])

  38.             # 左子树:使用 [l, mid-1]
  39.             node.left = buildBlancedBST(l, mid - 1)

  40.             # 右子树:使用 [mid+1, r]
  41.             node.right = buildBlancedBST(mid + 1, r)

  42.             return node

  43.         # Step 2: 用整个有序数组构建平衡 BST
  44.         new_root = buildBlancedBST(0, len(vals_list) - 1)
  45.         return new_root
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-10 12:27:12 | 只看该作者
全局:
LC 283. Move Zeroes

283. 移动零 - 核心总结
🎯 题目要求

将所有 0 移到数组末尾
保持非零元素的相对顺序
原地修改(不能创建新数组)

💡 核心思路:双指针分区
本质:通过 slow 和 fast 指针,把非零元素不断向前压缩
pythonslow = 0  # 慢指针:下一个非零元素应该放的位置

for fast in range(len(nums)):  # 快指针:遍历寻找非零元素
    if nums[fast] != 0:
        if slow != fast:  # 优化:避免自我交换
            nums[slow], nums[fast] = nums[fast], nums[slow]
        slow += 1
```

## 📊 指针含义

| 指针 | 作用 |
|------|------|
| `slow` | 维护"非零区域"的边界,指向下一个非零元素的位置 |
| `fast` | 探索整个数组,寻找非零元素 |

## 🔍 数组分区(任意时刻)
```
[非零元素区 | 零元素区 | 未处理区]
↑          ↑        ↑
0        slow     fast

[0, slow):已处理的非零元素
[slow, fast):已处理的零
[fast, end):未处理

⚡ 复杂度

时间:O(n) - 一次遍历
空间:O(1) - 原地修改

🔑 关键点

slow 始终指向下一个非零元素该放的位置
fast 找到非零元素就交换到 slow 位置
添加 if slow != fast 避免无用的自我交换

📝 记忆口诀

fast 探路找非零,swap 到 slow 再前进
  1. class Solution:
  2.     def moveZeroes(self, nums: List[int]) -> None:
  3.         """
  4.         Do not return anything, modify nums in-place instead.
  5.         """
  6.         slow = 0

  7.         for fast in range(len(nums)):
  8.             if nums[fast] != 0:
  9.                 if fast != slow:
  10.                     nums[fast], nums[slow] = nums[slow], nums[fast]
  11.                 slow += 1
  12.         
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-11 11:08:08 | 只看该作者
全局:
LC. 109. Convert Sorted List to Binary Search Tree
  1. # Definition for singly-linked list.
  2. # class ListNode:
  3. #     def __init__(self, val=0, next=None):
  4. #         self.val = val
  5. #         self.next = next
  6. # Definition for a binary tree node.
  7. # class TreeNode:
  8. #     def __init__(self, val=0, left=None, right=None):
  9. #         self.val = val
  10. #         self.left = left
  11. #         self.right = right
  12. class Solution:
  13.     def sortedListToBST(self, head: Optional[ListNode]) -> Optional[TreeNode]:
  14.         val_list = []
  15.         node = head

  16.         while node:
  17.             val_list.append(node.val)
  18.             node = node.next

  19.         def buildBalancedBST(l, r):
  20.             if l > r: return None

  21.             mid = (l+r)//2

  22.             node = TreeNode(val = val_list[mid])
  23.             node.left = buildBalancedBST(l, mid - 1)
  24.             node.right = buildBalancedBST(mid + 1, r)

  25.             return node

  26.         root = buildBalancedBST(0, len(val_list) - 1)

  27.         return root
  28.         
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-11 11:24:49 | 只看该作者
全局:
LC 2196. Create Binary Tree From Descriptions

经典题目,我非常喜欢这个题目,充分利用了数据结构来解决问题。

通过 node_dict, children—set,val-set 来构建树,同时 track 出 root。

自然高效。

利用哈希表快速构建树,并通过集合操作找到根节点。
  1. # Definition for a binary tree node.
  2. # class TreeNode:
  3. #     def __init__(self, val=0, left=None, right=None):
  4. #         self.val = val
  5. #         self.left = left
  6. #         self.right = right

  7. class Solution:
  8.     def createBinaryTree(self, descriptions: List[List[int]]) -> Optional[TreeNode]:
  9.         """
  10.         从描述数组构建二叉树
  11.         
  12.         核心思路:
  13.         1. 用哈希表存储所有节点(值 -> 节点对象)
  14.         2. 用集合记录所有子节点
  15.         3. 根节点 = 不在子节点集合中的节点
  16.         
  17.         时间复杂度:O(n) - n 是描述数量
  18.         空间复杂度:O(n) - 存储所有节点
  19.         """
  20.         # 存储所有节点:{节点值: TreeNode对象}
  21.         nodes = {}
  22.         
  23.         # 记录所有作为子节点的值(用于找根节点)
  24.         children = set()
  25.         
  26.         # 遍历所有父子关系描述
  27.         for parent_val, child_val, is_left in descriptions:
  28.             # 创建或获取父节点
  29.             if parent_val not in nodes:
  30.                 nodes[parent_val] = TreeNode(parent_val)
  31.             
  32.             # 创建或获取子节点
  33.             if child_val not in nodes:
  34.                 nodes[child_val] = TreeNode(child_val)
  35.             
  36.             # 建立父子关系
  37.             if is_left == 1:  # 左孩子
  38.                 nodes[parent_val].left = nodes[child_val]
  39.             else:  # 右孩子 (is_left == 0)
  40.                 nodes[parent_val].right = nodes[child_val]
  41.             
  42.             # 记录子节点
  43.             children.add(child_val)
  44.         
  45.         # 找根节点:遍历所有节点,找到不在 children 中的节点
  46.         for val in nodes:
  47.             if val not in children:
  48.                 return nodes[val]
  49.         
  50.         # 题目保证有效树,理论上不会执行到这里
  51.         return None
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-11 11:37:04 | 只看该作者
全局:
LC .  26. Remove Duplicates from Sorted Array
  1. class Solution:
  2.     def removeDuplicates(self, nums: List[int]) -> int:
  3.         """
  4.         双指针法:slow-fast
  5.         
  6.         思路:
  7.         - slow: 指向下一个不重复元素的位置
  8.         - fast: 遍历数组寻找新元素
  9.         - 当 nums[fast] != nums[slow-1] 时,说明找到新元素
  10.         
  11.         时间复杂度:O(n)
  12.         空间复杂度:O(1)
  13.         """
  14.         # 边界情况:空数组或单元素
  15.         if len(nums) <= 1:
  16.             return len(nums)
  17.         
  18.         # slow 初始化为 1(第一个元素必定保留)
  19.         slow = 1
  20.         
  21.         # fast 从索引 1 开始遍历
  22.         for fast in range(1, len(nums)):
  23.             # 如果当前元素与上一个不重复元素不同
  24.             if nums[fast] != nums[slow - 1]:
  25.                 nums[slow] = nums[fast]  # 放到 slow 位置
  26.                 slow += 1  # slow 前进
  27.         
  28.         return slow  # slow 就是不重复元素的个数
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-11 11:48:37 | 只看该作者
全局:
LC. 27. Remove Element
  1. class Solution:
  2.     def removeElement(self, nums: List[int], val: int) -> int:
  3.         """
  4.         双指针法:slow-fast
  5.         
  6.         思路:
  7.         - slow: 指向下一个保留元素的位置
  8.         - fast: 遍历数组寻找不等于 val 的元素
  9.         - 找到就放到 slow 位置,slow 前进
  10.         
  11.         时间复杂度:O(n)
  12.         空间复杂度:O(1)
  13.         """
  14.         slow = 0  # 慢指针:保留元素的边界
  15.         
  16.         for fast in range(len(nums)):
  17.             # 如果当前元素不等于 val,保留它
  18.             if nums[fast] != val:
  19.                 nums[slow] = nums[fast]
  20.                 slow += 1
  21.         
  22.         return slow  # slow 就是保留元素的个数
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-11 11:56:38 | 只看该作者
全局:
LC 80. Remove Duplicates from Sorted Array II

双指针(slow-fast)
关键观察:

每个元素最多保留 2 次
数组已排序 → 重复元素必定相邻
判断标准:当前元素是否与 slow-2 位置的元素相同

核心逻辑:
如果 nums[fast] != nums[slow-2]:
    说明当前元素最多出现了 1 次,可以保留
否则:
    说明已经有 2 个相同元素了,跳过
  1. class Solution:
  2.     def removeDuplicates(self, nums: List[int]) -> int:
  3.         """
  4.         双指针法:允许最多保留 2 个重复元素
  5.         
  6.         核心思想:
  7.         - slow: 指向下一个元素应该放置的位置
  8.         - fast: 遍历数组
  9.         - 判断条件:nums[fast] 与 nums[slow-2] 比较
  10.         
  11.         为什么比较 slow-2?
  12.         - slow-1: 上一个放入的元素
  13.         - slow-2: 上上个放入的元素
  14.         - 如果 nums[fast] == nums[slow-2],说明已经有 2 个相同的了
  15.         
  16.         时间复杂度:O(n)
  17.         空间复杂度:O(1)
  18.         """
  19.         # 边界情况:长度 <= 2 的数组直接返回
  20.         if len(nums) <= 2:
  21.             return len(nums)
  22.         
  23.         # slow 从索引 2 开始(前两个元素必定保留)
  24.         slow = 2
  25.         
  26.         # fast 从索引 2 开始遍历
  27.         for fast in range(2, len(nums)):
  28.             # 如果当前元素与 slow-2 位置不同
  29.             # 说明当前元素最多出现了 1 次,可以保留
  30.             if nums[fast] != nums[slow - 2]:
  31.                 nums[slow] = nums[fast]
  32.                 slow += 1
  33.         
  34.         return slow
复制代码
通用解法:保留 k 个重复元素

关键点:

✅ 前 k 个元素必定保留(slow 从 k 开始)
✅ 比较 nums[fast] 和 nums[slow-k]
✅ 利用有序性:重复元素必定相邻
✅ 通用模板:可扩展到保留 k 次


  1. class Solution:
  2.     def removeDuplicates(self, nums: List[int], k: int = 2) -> int:
  3.         """
  4.         通用解法:最多保留 k 个重复元素
  5.         
  6.         k=1: 每个元素最多 1 次(题目 26)
  7.         k=2: 每个元素最多 2 次(题目 80)
  8.         k=3: 每个元素最多 3 次
  9.         """
  10.         if len(nums) <= k:
  11.             return len(nums)
  12.         
  13.         slow = k  # 前 k 个元素必定保留
  14.         
  15.         for fast in range(k, len(nums)):
  16.             # 与 slow-k 位置比较
  17.             if nums[fast] != nums[slow - k]:
  18.                 nums[slow] = nums[fast]
  19.                 slow += 1
  20.         
  21.         return slow
复制代码
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表