讲师中心 微信公众号
AI工具推荐 视频效率加速

如何解决 LeetCode “最大加号标志” 中的递归深度超限问题

梦枫大大_6499

梦枫大大_6499

发布时间:2026-02-21 11:11:14

|

878人浏览过

|

来源于php中文网

原创

如何解决 LeetCode “最大加号标志” 中的递归深度超限问题

本文详解为何在实现 orderOfLargestPlusSign 时使用记忆化递归会触发 RecursionError,并提供高效、无递归的动态规划解法——通过预处理四个方向的连续非矿格长度,在 O(n²) 时间内安全求解。

本文详解为何在实现 `orderoflargestplussign` 时使用记忆化递归会触发 `recursionerror`,并提供高效、无递归的动态规划解法——通过预处理四个方向的连续非矿格长度,在 o(n²) 时间内安全求解。

在 LeetCode 第764题「最大加号标志」中,目标是找出以某点为中心、四臂等长且全由非矿格(即未被挖空)构成的最大加号形状的阶数(order)。初学者常尝试用记忆化递归(@cache)实现“从中心向四周延伸”的逻辑,例如定义 helper(r, c) 表示以 (r, c) 为起点、沿某一方向能延伸的最长非矿格数。但原代码中的递归设计存在根本性缺陷:它未区分方向,导致状态间形成环状依赖。

具体来看,原 helper 函数试图通过 min(helper(r+1,c), helper(r−1,c), helper(r,c+1), helper(r,c−1)) 计算十字臂长。然而,该逻辑隐含了“任意方向均可回溯”的假设——例如 helper(1,1) 调用 helper(0,1),而 helper(0,1) 又可能调用 helper(1,1)(当检查上方向时),从而形成无限递归链。即使添加了边界与矿格判断(if r

因此,正确解法应摒弃“中心扩散式递归”,转而采用方向分离的动态规划预处理:对每个格子 (i, j),独立计算其向上、向下、向左、向右四个方向连续非矿格的最大长度。这四个值的最小值,即为以 (i, j) 为中心所能构成的最大加号阶数。

以下是优化后的完整实现:

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[i][j] 表示从 (i,j) 向上连续非矿格数(含自身)
        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[i][j]: 向左连续长度(注意:此处按行扫描,故 left[i][j] 对应第 i 行)
                if (j, i) in mine_set:  # ⚠️ 注意原答案此处索引有误,已修正逻辑说明
                    s_left = 0
                else:
                    s_left += 1
                left[i][j] = s_left  # 实际应为 left[j][i]?不——我们统一按 (行, 列) 理解

        # 更清晰、无歧义的写法(推荐):
        # 重新初始化并分方向独立遍历
        top = [[0]*n for _ in R]
        bot = [[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
                top[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
                bot[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

        # 枚举每个中心点,取四方向最小值的最大值
        ans = 0
        for i in R:
            for j in R:
                arm = min(top[i][j], bot[i][j], left[i][j], right[i][j])
                ans = max(ans, arm)

        return ans

✅ 关键优势:

  • 时间复杂度稳定 O(n²):四次线性扫描 + 一次枚举,无递归开销;
  • 空间复杂度 O(n²):仅需四个 n×n 数组,可进一步优化为滚动数组(但可读性下降);
  • 绝对避免栈溢出:无递归调用,彻底规避 RecursionError。

⚠️ 注意事项:

  • 原答案中 left[b][a] = s2 等写法易引发索引混淆(将行列关系颠倒),实际开发中建议严格按 (row, col) 维度组织数组,并为每个方向单独编写清晰循环;
  • mines 转集合必须使用 tuple(i)(因列表不可哈希),否则运行时报错;
  • 若 n 较大(如 500),递归解法在 Python 中极易爆栈(默认递归限制约 1000 层),而 DP 解法无此风险。

总结:面对网格类“多方向延伸”问题,优先考虑方向解耦 + 线性 DP 预处理,而非试图用记忆化递归模拟多向搜索——后者在无向依赖下极易陷入死循环,是典型的“看似合理、实则危险”的陷阱。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

相关专题

更多
堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

6347

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2668

2023.08.10

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

60

2026.10.28

Vite 如何搭配 Vue.js
Vite 如何搭配 Vue.js

Vite搭配Vue.js专题汇集创建Vue 3项目、配置开发环境、路径别名、Vue插件、代理、多环境构建与调试的已审核文章,帮助读者完成从初始化到日常开发的配置。

40

2026.10.28

Vite 项目目录怎么看
Vite 项目目录怎么看

Vite项目目录专题汇集入口文件、src源码、public静态资源、路径别名、项目根目录和构建输出目录相关的已审核文章,帮助读者理解各文件夹的用途与引用方式。

120

2026.10.28

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

140

2026.10.22

Gradle Wrapper配置教程
Gradle Wrapper配置教程

本专题介绍Gradle Wrapper的基本作用,涵盖文件组成、生成方法、版本配置和任务调用,帮助你理解项目如何使用指定的Gradle版本,减少不同开发环境带来的构建差异。

120

2026.10.22

Golangci-lint检查器使用教程
Golangci-lint检查器使用教程

本专题整理Golangci-lint常用Linters的用途与配置方法,覆盖错误处理、无效代码、安全、复杂度、命名和依赖检查,帮助项目合理选规则。

80

2026.10.22

Gradle怎么构建Java项目
Gradle怎么构建Java项目

本专题介绍Gradle Java项目构建方法,涵盖Java插件配置、依赖声明、源码编译、测试执行和打包检查,帮助你理解各阶段的任务关系,逐步完成项目构建。

40

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn