news 2026/4/16 17:25:03

从零开始写算法——回溯篇3:括号生成 + 单词搜索

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零开始写算法——回溯篇3:括号生成 + 单词搜索

回溯算法(DFS)是算法面试中的重难点。很多同学觉得它难,是因为分不清什么时候该“恢复现场”,什么时候该“标记状态”。

今天我们通过两道经典的 LeetCode 题目——括号生成单词搜索,来对比分析回溯算法的两种不同模式:路径构造模式网格搜索模式

一、 路径构造模式:括号生成 (Generate Parentheses)

这道题是典型的“做选择”问题。你可以把它想象成手里拿着 n 个左括号和 n 个右括号,在 $2n$ 个空位上做填空题。

核心逻辑

我们在递归时需要时刻遵守两个规则,才能保证生成的括号是合法的:

  1. 手里还有存货才能放:只要左括号没用完 (left < n),就可以尝试放左括号。

  2. 不能欠债才能还:只有当前放下的左括号多于右括号时 (right < left),也就是有未闭合的左括号时,才能放右括号。

代码实现

这里使用了标准的push_backpop_back写法。这种写法的核心在于我们是在构造参数vals),每次递归前把东西放进去,递归回来后必须把它拿出来,恢复成原来的样子,以便进行下一次选择。

C++代码实现:

class Solution { // 手里拿着 n 个左括号和 n 个右括号,做填空题,时刻遵守两个规则 // 规则1 : left < n 规则2 : right < left vector<string> ans; void dfs(string& vals, int left, int right, int n) { // Base Case: 左右都用完了,说明构造完成 if (right == n) { ans.push_back(vals); return; } // 尝试放左括号 if (left < n) { vals.push_back('('); // 做选择 dfs(vals, left + 1, right, n); // 进入下一层 vals.pop_back(); // 撤销选择(恢复现场) } // 尝试放右括号 if (right < left) { vals.push_back(')'); // 做选择 dfs(vals, left, right + 1, n); // 进入下一层 vals.pop_back(); // 撤销选择(恢复现场) } } public: vector<string> generateParenthesis(int n) { string vals = ""; dfs(vals, 0, 0, n); return ans; } };

复杂度分析

  • 时间复杂度:O(4^n / sqrt(n))。

    • 这个复杂度与卡特兰数(Catalan Number)有关。简单理解,每个位置有两种选择,共有 2n 个位置,但因为有剪枝(规则限制),实际数量远小于 2^(2n)。

  • 空间复杂度:O(n)。

    • 主要消耗在递归调用栈和存储当前路径的vals字符串上,最大深度为 2n。


二、 网格搜索模式:单词搜索 (Word Search)

这道题属于“图/网格遍历”。与上一题不同,这里的“恢复现场”不是为了构造字符串,而是为了防止走回头路

核心逻辑

我们把每一个点作为起点,执行 DFS 搜索。

这里有一个非常关键的细节:什么时候标记节点?

  • 错误做法:在进入下一层递归前,标记下一个位置 (nx, ny)。这会导致起点无法被标记,以及逻辑判断复杂化。

  • 正确做法标记当前位置 (x, y)。也就是“进门后再锁门”。一旦进入 DFS 函数,先判断当前点是否匹配,匹配的话就标记为已访问(比如改为.),递归结束后再改回来。

代码实现

注意看代码中的注释,关于恢复现场和循环变量的处理是易错点。

C++代码实现:

class Solution { // 思路: 把每一个点作为起点然后对他执行dfs去遍历搜索是否存在这样的单词 // 每次上下左右找之前,把自己当前位置xy标记为. 注意是当前位置不是下一个位置 如果改的是nxny,那么下一步进去就无法通过borad[x][y] 和 word判断了 // 注意: 既然要恢复现场就要提前记录w,注意for里面不要用变量i会冲突 int dx[4] = {0, 1, 0, -1}; int dy[4] = {-1, 0, 1, 0}; bool ans; // i 代表当前匹配到了 word 的第几个字符 bool dfs(vector<vector<char>>& board, string word, int n, int i, int x, int y) { // 1. 判断当前格子字符是否匹配 if (word[i] != board[x][y]){ return false; } // 2. 匹配完成 if (i == n - 1) return true; // 3. 标记当前节点 (Backtracking 核心) // 提前记录便于恢复现场 char w = board[x][y]; // 避免重复使用到,标记为 '.' board[x][y] = '.'; // 4. 遍历四个方向 // 注意这里不能用 i 做为变量名(会与参数 i 冲突) for (int j = 0; j < 4; ++j) { int nx = x + dx[j]; int ny = y + dy[j]; // 越界检查及是否已访问检查 if (nx < 0 || nx >= board.size() || ny < 0 || ny >= board[0].size() || board[nx][ny] == '.') { continue; } // 只要有一条路走通了,就直接返回 true if (dfs(board, word, n, i + 1, nx, ny)) return true; } // 5. 恢复现场 board[x][y] = w; return false; } public: bool exist(vector<vector<char>>& board, string word) { int n = word.size(); for (int i = 0; i < board.size(); ++i) { for (int j = 0; j < board[0].size(); ++j) { // 以每个格子为起点尝试 ans = dfs(board, word, n, 0, i, j); if (ans == true) return ans; } } return ans; } };

复杂度分析

  • 时间复杂度:O(M * N * 3^L)。

    • M 和 N 是网格的长宽,我们需要遍历每一个格子作为起点(M * N)。

    • L 是字符串word的长度。在 DFS 函数中,除了第一次有 4 个分支,之后因为不能走回头路,最多只有 3 个分支。所以是 3^L。

  • 空间复杂度:O(L)。

    • 空间消耗主要来自递归栈的深度,最大深度也就是单词的长度 L。


总结

通过这两道题,我们可以总结出 DFS 回溯的两个黄金法则:

  1. 构造类回溯(括号生成):目的是拼凑出一个结果。我们通过push_back添加元素进入下一层,出来后pop_back撤销。

  2. 搜索类回溯(单词搜索):目的是在图中寻找路径。我们通过修改board[x][y]为特殊字符来标记“当前正在访问”,递归结束后还原字符,以免影响其他路径的搜索。

记住:每一个 DFS 函数只负责管理自己脚下的节点(标记和恢复),不要试图去管理下一层节点的标记,否则容易出现逻辑死循环。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 10:52:54

计算机毕业设计springboot酒店管理系统 基于SpringBoot的宾馆业务综合管理平台 融合SpringBoot框架的智慧旅店运营系统

计算机毕业设计springboot酒店管理系统h4v57 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。当线下入住高峰与线上即时预订叠加&#xff0c;传统的手工排房、纸质登记、人工对账…

作者头像 李华
网站建设 2026/4/16 14:27:33

基于python的就业网站可视化系统设计与实现 计算机毕业设计选题 计算机毕设项目 前后端分离【源码-文档报告-代码讲解】

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…

作者头像 李华
网站建设 2026/4/13 5:56:34

Let‘s Encrypt HTTPS 证书配置指南

# Lets Encrypt HTTPS 证书配置指南本指南用于在 Amazon Linux 2023 系统上使用 Lets Encrypt 免费证书为 Nginx 配置 HTTPS。## 前置条件- 系统&#xff1a;Amazon Linux 2023 - Web 服务器&#xff1a;Nginx - 域名已正确解析到服务器 IP - 防火墙已开放 80 和 443 端口## 配…

作者头像 李华
网站建设 2026/4/16 12:25:23

基于Spring Boot的心理咨询预约微信小程序(源码+论文+部署+安装)

感兴趣的可以先收藏起来&#xff0c;还有在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望可以帮到大家。一、程序背景随着现代社会生活节奏加快&#xff0c;人们心理压力日益增大&#xff0c;心理健康问题成为社…

作者头像 李华
网站建设 2026/4/16 12:58:53

花16800元买线索,不如花768元找老板

在B2B的销售与采购这个领域里面&#xff0c;正在上演着一个极其残酷的情况&#xff1a; 有非常多的企业&#xff0c;每一年都会花费上万元去订阅那些价格昂贵的拓客系统&#xff0c;像探迹、励销云这些都属于此类&#xff0c;然而他们拿到的却只是大量没有用处的名单。 这些名单…

作者头像 李华
网站建设 2026/4/16 16:24:10

创客匠人 AI 智能体重构知识变现生态:创始人 IP 的商业闭环搭建指南

当知识付费市场规模逼近 3000 亿元&#xff0c;用户却越来越 “挑剔”——52.4% 的受访青年认为知识付费 “宣传与实际不符”&#xff0c;54.3% 抱怨缺乏后续服务。这一矛盾背后&#xff0c;是传统知识变现模式的底层缺陷&#xff1a;流量驱动下的 “一锤子买卖”&#xff0c;难…

作者头像 李华