解决 LeetCode「最大加号标志」递归超深度问题:从错误递归到动态规划优化

1次阅读

解决 LeetCode「最大加号标志」递归超深度问题:从错误递归到动态规划优化

本文详解为何在实现「最大加号标志」问题时使用记忆化递归会触发 recursionerror,指出其根本原因在于无方向约束导致无限回溯,并提供高效、可扩展的四向预处理动态规划解法。

本文详解为何在实现「最大加号标志」问题时使用记忆化递归会触发 recursionerror,指出其根本原因在于无方向约束导致无限回溯,并提供高效、可扩展的四向预处理动态规划解法。

leetcode 第 764 题「最大加号标志」中,目标是找出以某点为中心、四个方向(上、下、左、右)延伸出的连续非矿格(即非 mines 中坐标)所能构成的最大「加号」阶数(order)。初学者常尝试用记忆化递归(如 @cache + DFS)直接模拟“从中心向外延展”,但很快会遇到 RecursionError: maximum recursion depth exceeded —— 即使已添加边界和矿格检查,也依然失败。

根本原因在于逻辑缺陷:递归未建模「方向性」
观察原代码中的 helper(r, c):

def helper(r,c):     if r >= n or r < 0 or c < 0 or c >= n or (r,c) in mines: return 0     return 1 + min(         helper(r + 1, c),  # 下         helper(r - 1, c),  # 上         helper(r, c + 1),  # 右         helper(r, c - 1)   # 左     )

该函数试图表达:“以 (r,c) 为起点,能向四个方向各延伸多远?取最小值即为该点的加号阶数”。但问题在于:helper(r+1,c) 会调用 helper(r,c)(因为向上走一步就回到原点),而 helper(r,c) 又再次调用 helper(r+1,c)……形成 无终止的双向依赖环。即使有 @cache,缓存的是 (r,c) 的返回值,但该值本身依赖于尚未计算完成的邻居——而由于无方向约束,递归图存在环,导致深度线性增长直至溢出。

✅ 正确思路:将“中心向外延伸”转化为“单向累积统计”
我们不再对每个点做四向 DFS,而是预处理四个方向的连续安全格长度

  • top[i][j]:从第 i 行第 j 列向上(含自身)最多有多少个连续非矿格;
  • bot[i][j]:向下;
  • left[i][j]:向左;
  • right[i][j]:向右。

每个方向均可通过一次扫描 O(n²) 完成,且天然无环、无回溯。

以下是优化后的完整实现(已适配 Python 3.9+,使用 range(n) 和解包语法):

class Solution:     def orderOfLargestPlusSign(self, n: int, mines: List[List[int]]) -> int:         # 将 mines 转为集合,支持 O(1) 查找         mine_set = {tuple(mine) for mine in mines}         R = range(n)          # 初始化四向 DP 数组         top = [[0] * n for _ in R]         bot = [[0] * n for _ in R]         left = [[0] * n for _ in R]         right = [[0] * n for _ in R]          # ? 从左到右、从上到下:计算 top 和 left         for i in R:             s_top = s_left = 0             for j in R:                 # top[i][j]:第 i 行、第 j 列向上连续安全格数(含本格)                 if (i, j) in mine_set:                     s_top = 0                 else:                     s_top += 1                 top[i][j] = s_top                  # left[j][i]:注意此处巧妙转置——用于后续计算第 i 列从上到下的 left 累积?                 # 实际更清晰的做法是分开两层循环;下方采用标准四向扫描法重写以增强可读性         # ✅ 推荐更清晰的四次独立扫描(见下方修正版)

为提升可读性与正确性,我们采用四次明确方向的线性扫描

class Solution:     def orderOfLargestPlusSign(self, n: int, mines: List[List[int]]) -> int:         mine_set = {tuple(mine) for mine in mines}         R = range(n)          # 四个方向 DP 表:dp_dir[i][j] 表示从 (i,j) 出发沿 dir 方向的连续安全格数量(含自身)         up = [[0] * n for _ in R]         down = [[0] * n for _ in R]         left = [[0] * n for _ in R]         right = [[0] * n for _ in R]          # ? 向上扫描:每列从上到下累积         for j in R:             cnt = 0             for i in R:                 if (i, j) in mine_set:                     cnt = 0                 else:                     cnt += 1                 up[i][j] = cnt          # ? 向下扫描:每列从下到上累积         for j in R:             cnt = 0             for i in reversed(R):                 if (i, j) in mine_set:                     cnt = 0                 else:                     cnt += 1                 down[i][j] = cnt          # ◀️ 向左扫描:每行从左到右累积         for i in R:             cnt = 0             for j in R:                 if (i, j) in mine_set:                     cnt = 0                 else:                     cnt += 1                 left[i][j] = cnt          # ▶️ 向右扫描:每行从右到左累积         for i in R:             cnt = 0             for j in reversed(R):                 if (i, j) in mine_set:                     cnt = 0                 else:                     cnt += 1                 right[i][j] = cnt          # ? 对每个位置 (i,j),其最大加号阶数 = 四方向最小值         ans = 0         for i in R:             for j in R:                 cross = min(up[i][j], down[i][j], left[i][j], right[i][j])                 ans = max(ans, cross)         return ans

关键优势总结:

  • ✅ 时间复杂度:O(n²),远优于错误递归的指数/无限复杂度;
  • ✅ 空间复杂度:O(n²),可接受(n ≤ 500);
  • ✅ 无递归调用,彻底规避栈溢出风险;
  • ✅ 逻辑清晰、可验证:每步扫描对应一个物理方向,易于调试与单元测试。

注意事项:

  • mines 必须转为 set(而非 list),否则 in 操作退化为 O(k),整体变慢;
  • 四个 DP 数组不可复用同一内存(如误用 [[0]*n]*n),需确保每行独立;
  • 边界处理已隐含在扫描逻辑中(cnt=0 重置),无需额外判断;
  • 若需空间优化,可只保留两个方向数组并滚动更新,但可读性下降,通常不必要。

掌握这种「将循环依赖递归重构为单向动态规划」的思维,是解决网格类 DFS 陷阱题的关键跃迁。

text=ZqhQzanResources