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

刷题记录帖子

🔗
 楼主| Myron2017 2026-2-6 00:08:15 | 只看该作者
全局:
本帖最后由 Myron2017 于 2026-2-5 10:32 编辑

LC 2789. Largest Element in an Array after Merge Operations

仔细读题,理解题干。

这个所谓的合并,归根到底就是如果右比左大,就向右合并。

为什么需要最后的更新? pythonmax_val = max(max_val, current)

两个原因:

循环可能一次都不进入 else

所有元素都能合并
max_val 从未更新,还是初始值
最后的累积可能是最大值

即使进入过 else
最后一段累积可能更大
记忆技巧
循环内更新 → 遇到障碍时记录 循环外更新 → 最后的成果也要记录

两次检查,确保万无一失!
  1. class Solution:
  2.     def maxArrayValue(self, nums: List[int]) -> int:
  3.         """
  4.         从右往左贪心合并
  5.         
  6.         核心思想:
  7.         - 右边的元素作为"吸收者"
  8.         - 左边的元素如果 ≤ 累积和,就被吸收
  9.         - 遇到无法吸收的元素,重新开始累积
  10.         """
  11.         n = len(nums)
  12.         
  13.         # 从最右边开始
  14.         current = nums[-1]  # 当前累积和(右边的"吸收池")
  15.         max_val = current   # 记录最大值
  16.         
  17.         # 从右往左遍历
  18.         for i in range(n - 2, -1, -1):
  19.             if nums[i] <= current:
  20.                 # 可以合并:nums[i] 被吸收
  21.                 current += nums[i]
  22.             else:
  23.                 # 不能合并:nums[i] 太大,无法被吸收
  24.                 # 更新最大值,nums[i] 成为新的起点
  25.                 max_val = max(max_val, current)
  26.                 current = nums[i]
  27.         
  28.         # 别忘了最后的累积和
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-6 01:05:07 | 只看该作者
全局:
LC 110. Balanced Binary Tree

A height-balanced binary tree is a binary tree in which the depth of the two subtrees of every node never differs by more than one.

平衡条件 :
左子树平衡
右子树平衡
左右子树差值不超过 1
  1. class Solution:
  2.     def isBalanced(self, root: Optional[TreeNode]) -> bool:
  3.         # 返回值含义:
  4.         #   >= 0 : 当前子树的高度
  5.         #   -1   : 当前子树已经不平衡(提前终止)
  6.         def height(node):
  7.             # 空节点高度为 0,是平衡的
  8.             if not node:
  9.                 return 0

  10.             # 递归计算左子树高度
  11.             left = height(node.left)
  12.             # 如果左子树已经不平衡,直接向上返回 -1
  13.             # 不再继续计算(剪枝,避免无意义计算)
  14.             if left == -1:
  15.                 return -1

  16.             # 递归计算右子树高度
  17.             right = height(node.right)
  18.             # 如果右子树已经不平衡,直接向上返回 -1
  19.             if right == -1:
  20.                 return -1

  21.             # 当前节点的平衡性判断:
  22.             # 左右子树高度差 > 1,则整棵子树不平衡
  23.             if abs(left - right) > 1:
  24.                 return -1

  25.             # 当前节点是平衡的
  26.             # 返回当前子树高度 = max(左, 右) + 1
  27.             return max(left, right) + 1

  28.         # 如果整棵树返回的不是 -1,说明是平衡二叉树
  29.         return height(root) != -1
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-6 01:49:55 | 只看该作者
全局:
LC 692. Top K Frequent Words

先统计每个单词 Freq
然后把单词压入堆栈
  1. class Solution:
  2.     def topKFrequent(self, words: List[str], k: int) -> List[str]:
  3.         # 1️⃣ 统计每个单词出现的频率
  4.         # Counter 返回一个 dict: {单词: 出现次数}
  5.         count = Counter(words)

  6.         # 2️⃣ 构建一个 heap(优先队列)
  7.         # heap 元素格式:(-频率, 单词)
  8.         # 频率取负号,是为了让 Python 的最小堆行为变成“最大堆”,
  9.         # 这样出现次数多的单词会排在堆顶
  10.         heap = [(-freq, word) for word, freq in count.items()]
  11.         heapq.heapify(heap)  # 原地将列表 heap 转化为堆

  12.         # 3️⃣ 从堆中依次取出 k 个最频繁的单词
  13.         ans = []
  14.         while len(ans) < k:
  15.             # heappop 弹出堆顶元素
  16.             # 因为我们存的是 (-频率, 单词),堆顶就是当前出现次数最多的单词
  17.             # [1] 取单词部分
  18.             ans.append(heapq.heappop(heap)[1])

  19.         # 4️⃣ 返回最终结果列表
  20.         return ans
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-6 03:05:53 | 只看该作者
全局:
735. Asteroid Collision

这道题目之前面试见过,结果今天我还是不会,大方向对了,但是细节还是需要自己理清。
说实话,写 code 的时候,可以积极加上注释,注释反而会帮助你理清思路和要点。

核心思路

用栈模拟:

栈顶保存最近向右的行星

遇到向左行星才可能发生碰撞

栈顶比当前小 → 栈顶爆炸 → 当前行星继续碰撞

栈顶比当前大 → 当前行星爆炸 → break

栈顶相等 → 两颗爆炸 → break

栈空或没有碰撞 → 当前行星存活 → 入栈
  1. class Solution:
  2.     def asteroidCollision(self, asteroids: List[int]) -> List[int]:
  3.         stack = []
  4.         for a in asteroids:
  5.             # 当前行星可能爆炸
  6.             while stack and stack[-1] > 0 and a < 0:
  7.                 if stack[-1] < -a:
  8.                     # 栈顶行星比当前小 → 栈顶爆炸,继续比较下一颗
  9.                     stack.pop()
  10.                     continue
  11.                 elif stack[-1] == -a:
  12.                     # 栈顶与当前相等 → 两颗都爆炸
  13.                     stack.pop()
  14.                 # 当前行星爆炸或者两颗相等 → 不入栈,退出循环
  15.                 break
  16.             else:
  17.                 # 栈为空 或者 栈顶不是向右行星 → 当前行星存活,入栈
  18.                 stack.append(a)
  19.         
  20.         return stack
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-6 04:10:37 | 只看该作者
全局:
LC 1648. Sell Diminishing-Valued Colored Balls

不能一个个求解,利用库存格,整块,整层求解。
  1. class Solution:
  2.     def maxProfit(self, inventory: List[int], orders: int) -> int:
  3.             MOD = 10**9 + 7
  4.         
  5.         # 1. 降序排序库存,方便每次卖最大的球
  6.         inventory.sort(reverse=True)
  7.         inventory.append(0)  # 哨兵,方便处理最后一批球
  8.         
  9.         ans = 0
  10.         n = len(inventory)
  11.         
  12.         for i in range(len(inventory) - 1):
  13.             # 当前库存值
  14.             cur_val = inventory[i]
  15.             # 下一个库存值
  16.             next_val = inventory[i+1]
  17.             # 当前层以上(包括自己)所有大于下一层的库存格都参与卖球
  18.             width = i + 1  # i+1库存格可以同时卖
  19.             # 当前库存层可以卖多少球
  20.             cnt = width * (cur_val - next_val)
  21.             
  22.             if orders >= cnt:
  23.                 # 可以一次卖掉这一层的全部球
  24.                 # 求连续整数和公式:sum = (cur_val + next_val + 1) * (cur_val - next_val) // 2
  25.                 total = width * (cur_val + next_val + 1) * (cur_val - next_val) // 2
  26.                 ans = (ans + total) % MOD
  27.                 orders -= cnt
  28.             else:
  29.                 # 卖不完整层,卖 orders 个球
  30.                 full_rows = orders // width  # 完整递减行
  31.                 remainder = orders % width   # 剩下的零散球
  32.                 # 卖完整行的球
  33.                 total = width * (cur_val + cur_val - full_rows + 1) * full_rows // 2
  34.                 ans = (ans + total) % MOD
  35.                 # 卖剩下的零散球,每颗价值 = cur_val - full_rows
  36.                 ans = (ans + remainder * (cur_val - full_rows)) % MOD
  37.                 return ans  # 所有订单卖完,直接返回
  38.         
  39.         return ans



  40.         
复制代码
TLE 解法注意
  1. """
  2.         TLE

  3.         if len(inventory) == 1:
  4.             ans = ((inventory[0] - orders + 1) + inventory[0]) * orders // 2
  5.             return ans % (10**9+7)
  6.         #inventory = ordered(inventory, reverse=True)
  7.         k = 0
  8.         ans = 0
  9.         heap = [-i for i in inventory]
  10.         heapq.heapify(heap)
  11.         while k < orders:
  12.             curr = heapq.heappop(heap)
  13.             k += 1
  14.             ans += -curr
  15.             ans %= (10**9+7)
  16.             heapq.heappush(heap, curr + 1)
  17.         return ans
  18.         """
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-6 05:42:03 | 只看该作者
全局:
LC 567. Permutation in String

字典指纹比对法
  1. class Solution:
  2.     def checkInclusion(self, s1: str, s2: str) -> bool:
  3.         freq_s1 = Counter(s1)
  4.         n1 = len(s1)

  5.         for i in range(len(s2)-n1+1):
  6.             #print(i)
  7.             freq_curr = Counter(s2[i:i+n1])
  8.             #print(freq_curr, freq_s1)
  9.             if freq_curr == freq_s1: return True
  10.         return False
复制代码
滑动窗口比对法
  1. class Solution:
  2.     def checkInclusion(self, s1: str, s2: str) -> bool:
  3.         s2_len, s1_len = len(s2), len(s1)

  4.         if s1_len > s2_len:
  5.             return False

  6.         s1_char_freq_arr = [0] * 26
  7.         s2_window_char_freq_arr = [0] * 26

  8.         for index in range(s1_len):
  9.             s1_char_freq_arr[ord(s1[index]) - ord('a')] += 1
  10.             s2_window_char_freq_arr[ord(s2[index]) - ord('a')] += 1

  11.         if s1_char_freq_arr == s2_window_char_freq_arr:
  12.             return True

  13.         for index in range(s2_len - s1_len):
  14.             s2_window_char_freq_arr[ord(s2[index]) - ord('a')] -= 1
  15.             s2_window_char_freq_arr[ord(s2[index + s1_len]) - ord('a')] += 1

  16.             if s1_char_freq_arr == s2_window_char_freq_arr:
  17.                 return True

  18.         return False
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-6 06:17:56 | 只看该作者
全局:
LC 438. Find All Anagrams in a String

类似上面解法 HashMap 做字符串指纹
  1. class Solution:
  2.     def findAnagrams(self, s: str, p: str) -> List[int]:
  3.         freq = Counter(p)
  4.         n_s = len(s)
  5.         n_p = len(p)

  6.         ans = []

  7.         for i in range(n_s - n_p + 1):
  8.             curr_freq = Counter(s[i:i+n_p])
  9.             if curr_freq == freq: ans.append(i)
  10.         return ans
  11.         
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-6 07:14:52 | 只看该作者
全局:
LC 1027. Longest Arithmetic Subsequence

LC 1027. 最长等差数列总结1. 核心模型:动态规划 (DP)状态定义: dp[i][diff] 表示以 nums[i] 结尾、公差为 diff 的最长等差子序列的长度。转移方程: dp[i][diff] = dp[j][diff] + 1 (其中 $j < i, diff = nums[i] - nums[j]$)。初始化: 所有可能的序列长度至少为 $2$(两个数即可成数列)。2. 实现技巧容器选择:字典版: dp = [{} for _ in range(n)]。代码简洁,自动处理负数 diff。数组版: dp = [[1] * 1001 for _ in range(n)]。性能更高,需加 OFFSET = 500 防止索引越界。复杂度:时间:$O(n^2)$空间:$O(n \cdot \text{Range})$,其中 $\text{Range}$ 是公差的取值范围。3. 易错点 & 坑子序列不连续: 记得这不是子数组,必须遍历 $i$ 之前所有的 $j$,而不是只看 $i-1$。公差正负: 等差数列可以递增($diff > 0$)、递减($diff < 0$)或全等($diff = 0$)。最值更新: 在转移过程中实时维护一个 max_length。4. 复习口诀定结尾,定公差;双层循环 $i$ 找 $j$;查旧表,加一数;数组记得加 Offset。这一类“最长子序列”问题通常都要定义“以 $i$ 结尾”来破局。需要我为你推荐一道类似的题目(比如 LC 300 最长递增子序列)来巩固这个思维吗?
  1. class Solution:
  2.     def longestArithSeqLength(self, nums: list[int]) -> int:
  3.         n = len(nums)
  4.         if n <= 2:
  5.             return n
  6.         
  7.         # dp[i] 是一个字典,key 是公差 diff,value 是该公差下的最长长度
  8.         dp = [{} for _ in range(n)]
  9.         max_length = 0
  10.         
  11.         # 遍历数组中的每一个数作为当前的结尾
  12.         for i in range(n):
  13.             # 遍历 i 之前的所有数,寻找可以接上的前序节点
  14.             for j in range(i):
  15.                 diff = nums[i] - nums[j]
  16.                
  17.                 # 如果 j 处已经存在公差为 diff 的序列,则长度 +1
  18.                 # 否则,nums[j] 和 nums[i] 构成一个长度为 2 的新序列
  19.                 # dict.get(key, default) 是面试中常用的简洁写法
  20.                 dp[i][diff] = dp[j].get(diff, 1) + 1
  21.                
  22.                 # 记录全局最大值
  23.                 if dp[i][diff] > max_length:
  24.                     max_length = dp[i][diff]
  25.                     
  26.         return max_length
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-6 07:25:00 | 只看该作者
全局:
本帖最后由 Myron2017 于 2026-2-5 17:27 编辑

LC 300. Longest Increasing Subsequence

类似上一题,

这一类“最长子序列”问题通常都要定义“以 i 结尾”来破局。
程序员视角下的“解题直觉”
  • 看到 子序列 ===> 优先想 DP。
  • 看到 子数组 ===> 优先想 滑动窗口、双指针 或 贪心。
  1. class Solution:
  2.     def lengthOfLIS(self, nums: List[int]) -> int:
  3.         # dp[i] longest increasing subsequence ending with i
  4.         # if nums[i] > nums[j]: dp[i] = max(dp[j] + 1, dp[i])
  5.         n = len(nums)
  6.         dp = [1] * n

  7.         for i in range(n):
  8.             for j in range(i):
  9.                 if nums[i] > nums[j]:
  10.                     dp[i] = max(dp[j]+1, dp[i])

  11.         return max(dp)   
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-2-6 10:45:45 | 只看该作者
全局:
LC 424. Longest Repeating Character Replacement

这道题可以总结为以下几句“通关口诀”,帮你快速在大脑中建立索引:核心逻辑:窗口的本质是 “众数 + 替换额度”。窗口长度减去出现次数最多的字符频率,就是你需要替换的次数。滑动策略:窗口采用 “只扩不缩” 的贪心策略。合法时,右指针右移扩大窗口;非法时,左右指针同步右移,维持当前最大窗口进行平移。性能关键:不需要实时寻找当前窗口的绝对众数,只需维护一个 “历史最高频次” (max_freq)。因为只有出现更高频次的字符,才可能突破当前记录,产生更长的结果。一句话概括:用滑动窗口框住一个区间,只要“非众数”的个数不超过 k,窗口就可以继续野蛮生长。
  1. from collections import defaultdict

  2. class Solution:
  3.     def characterReplacement(self, s: str, k: int) -> int:
  4.         freq_w = defaultdict(int)
  5.         l, r = 0, 0
  6.         max_len = 0
  7.         
  8.         # 优化点:维护一个变量记录历史窗口中的最高频次
  9.         # 这样就不需要每次都去遍历字典找最大值了
  10.         max_freq_ch = 0

  11.         while r < len(s):
  12.             # 1. 进窗:先把右边的字符加入统计
  13.             freq_w[s[r]] += 1
  14.             
  15.             # 实时更新最高频次 (O(1) 时间复杂度)
  16.             max_freq_ch = max(max_freq_ch, freq_w[s[r]])
  17.             
  18.             # 计算当前窗口状态
  19.             len_w = r - l + 1
  20.             changes = len_w - max_freq_ch
  21.             
  22.             # 2. 分情况讨论
  23.             if changes <= k:
  24.                 # 情况A:替换 k 次以内能搞定 -> 窗口合法
  25.                 # 策略:更新结果,并尝试扩大窗口 (只动 r)
  26.                 max_len = max(max_len, len_w)
  27.                 r += 1
  28.             else:
  29.                 # 情况B:替换 k 次也不够 -> 窗口非法
  30.                 # 策略:窗口整体平移 (l 和 r 都动)
  31.                 # 既然是平移,左边出去一个,右边进来一个(右边进来的逻辑在下一轮循环开头)
  32.                 freq_w[s[l]] -= 1  # 左边移出,计数减一
  33.                 l += 1             # 左指针右移
  34.                 r += 1             # 右指针右移,不用统计,在while开头统计
  35.                
  36.         return max_len
复制代码
回复

使用道具 举报

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

本版积分规则

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