【C++题解】DFS和BFS

4小时编码练习计划,专注于深度优先搜索(DFS)和广度优先搜索(BFS)这两种基本且强大的算法。

下午 (4小时): 搜索算法专题——DFS与BFS

DFS和BFS是图论和多种问题求解中的基石算法。深刻理解它们的原理、差异和代码实现模板,是提升算法能力的必经之路。

练习计划概览

  • 总时长: 约 4 小时

  • 核心目标:

    1. 掌握深度优先搜索(DFS)的递归回溯思想,能够写出解决“排列组合”和“可行性搜索”问题的代码模板。

    2. 掌握广度优先搜索(BFS)基于队列的层次遍历思想,熟练使用其解决“无权图最短路径”问题。

    3. 清晰地辨别DFS(找所有解/任意解)和BFS(找最优解/最短步数)的典型应用场景。

    4. 学会使用visited数组或哈希表来标记状态,避免重复搜索和死循环。


第一部分:深度优先搜索 (DFS)——“不撞南墙不回头” (约 1.5 小时)

DFS的核心思想是“一条路走到黑”,它沿着一个分支深入探索,直到无法再前进时才回溯到上一步,换个方向继续探索。它天然地适合用递归实现,常用于求解所有可能的方案。

题目编号题目名称核心知识点练习目标
LeetCode 46 /
Luogu P1706
全排列 (Permutations)DFS, 回溯, 状态管理DFS入门必做题。练习最基础的DFS回溯模板。学习如何通过 visited 数组记录哪些数字已被使用,在递归的每一层选择一个未使用过的数字,并在回溯时恢复现场(pop_backvisited[i] = false)。
LeetCode 51 /
Luogu P1219
N皇后 (N-Queens)DFS, 回溯, 剪枝在“全排列”的基础上,增加了复杂的约束条件(行、列、对角线不能冲突)。这道题能极好地锻炼你如何在DFS的递归过程中进行“剪枝”——即提前判断当前路径是否合法,从而避免无效的深入搜索,提升效率。
题解
// 46 - 经典的使用DFS回溯
/*代码框架bool visited[MAXN]; // 访问标记数组void dfs(int u) {visited[u] = true; // 标记已访问// process(u); // 处理当前节点 u 的逻辑for (int v : adj[u]) { // 遍历 u 的所有邻居 vif (!visited[v]) { // 如果邻居 v 未被访问dfs(v); // 继续深入}}}
*/
#include <iostream>
#include <vector>using namespace std;vector<int> path;
vector<vector<int>> ans;
void bt(vector<int>& nums,vector<bool> &used){if(path.size()==nums.size()){ans.push_back(path);return;}for(int i=0;i<nums.size();i++){if(!used[i]){used[i] = true;path.push_back(nums[i]);bt(nums,used);path.pop_back();used[i] = false;}}
}
vector<vector<int>> permute(vector<int>& nums) {vector<bool> used(nums.size(),false);bt(nums,used);return ans;
}int main(){vector<int> nums ={1,2,3};      //  样例输入。vector<vector<int>> rst = permute(nums);for(vector<int> v : rst){for(int i : v){cout << i << " ";}cout << endl;}return 0;
}
//51 - 特别经典的问题,同样回溯去做#include <iostream>
#include <vector>
using namespace std;class Solution {
private:vector<vector<string>> result;vector<string> board;// 检查在(row, col)位置放置皇后是否安全bool isSafe(int row, int col, int n) {// 检查同一列是否有皇后for (int i = 0; i < row; i++) {if (board[i][col] == 'Q') {return false;}}// 检查左上对角线for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {if (board[i][j] == 'Q') {return false;}}// 检查右上对角线for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {if (board[i][j] == 'Q') {return false;}}return true;}// 回溯函数void backtrack(int row, int n) {// 如果所有行都放置了皇后,找到一个解if (row == n) {result.push_back(board);return;}// 尝试在当前行的每一列放置皇后for (int col = 0; col < n; col++) {if (isSafe(row, col, n)) {// 放置皇后board[row][col] = 'Q';// 递归处理下一行backtrack(row + 1, n);// 回溯,移除皇后board[row][col] = '.';}}}public:vector<vector<string>> solveNQueens(int n) {// 初始化棋盘board = vector<string>(n, string(n, '.'));result.clear();// 从第0行开始回溯backtrack(0, n);return result;}
};
int main(){int n;cin >> n;Solution solution;vector<vector<string>> rst = solution.solveNQueens(n);cout << "共找到 " << rst.size() << " 种解法:" << endl << endl;for (int i = 0; i < rst.size(); i++) {cout << "解法 " << (i + 1) << ":" << endl;for (const string& row : rst[i]) {cout << row << endl;}cout << endl;}return 0;
}

练习建议:

  • 画递归搜索树:在做这两道题时,强烈建议您在纸上画出当N较小(如3或4)时的递归搜索树。这能非常直观地帮助您理解“深入”、“回溯”、“剪枝”这些概念是如何在代码中体现的。

  • 模板化:尝试将“全排列”的代码抽象成一个通用的回溯模板:

    void dfs(参数) {if (满足终止条件) {// 存放结果return;}for (选择 : 本层可选的集合) {// 处理节点dfs(路径, 选择列表);// 回溯,撤销处理}
    }
    

第二部分:广度优先搜索 (BFS)——“一层一层地毯式搜索” (约 2 小时)

BFS借助队列实现,从起点开始,先访问所有距离为1的邻居,再访问所有距离为2的邻居,以此类推,像水波纹一样向外扩散。这个特性决定了它能够找到从起点到任何其他点的最短路径(在边权为1的图中)。

题目编号题目名称核心知识点练习目标
LeetCode 994腐烂的橘子
(Rotting Oranges)
BFS, 队列, 多源BFS经典的网格BFS问题。此题是“多源BFS”的绝佳范例,即初始时有多个起点(腐烂的橘子)同时开始搜索。通过BFS的层次遍历,可以很自然地模拟出时间一分钟一分钟流逝、橘子一层一层腐烂的过程。
LeetCode 127单词接龙
(Word Ladder)
BFS, 隐式图, 字符串处理BFS求解最短路径的经典应用。这道题的难点在于图是“隐式”的——节点是单词,两个单词之间是否存在边需要我们自行判断(是否只差一个字母)。这要求我们在BFS过程中动态地寻找邻居节点,是BFS能力的进阶考验。
题解
//994 - BFS,使用队列作为核心观察目前广度搜索节点,然后一层一层去处理,每次都会记录这次队列的大小。#include <iostream>
#include <vector>
#include <deque>
#include <queue>
using namespace std;class Solution {
public:int orangesRotting(vector<vector<int>>& grid) {int m = grid.size();int n = grid[0].size();queue<pair<int, int>> q;int fresh = 0;// 找到所有腐烂的橘子,并统计新鲜橘子数量for(int i = 0; i < m; i++) {for(int j = 0; j < n; j++) {if(grid[i][j] == 2) {q.push({i, j});} else if(grid[i][j] == 1) {fresh++;}}}// 如果没有新鲜橘子,直接返回0if(fresh == 0) return 0;int minutes = 0;int directions[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};while(!q.empty()) {int size = q.size();bool hasRotten = false;// 处理当前层的所有腐烂橘子for(int i = 0; i < size; i++) {pair<int, int> curr = q.front();q.pop();int x = curr.first;int y = curr.second;// 检查四个方向for(int d = 0; d < 4; d++) {int nx = x + directions[d][0];int ny = y + directions[d][1];// 边界检查和新鲜橘子检查if(nx >= 0 && nx < m && ny >= 0 && ny < n && grid[nx][ny] == 1) {grid[nx][ny] = 2;  // 变腐烂q.push({nx, ny});fresh--;hasRotten = true;}}}// 如果这一轮有橘子腐烂,时间+1if(hasRotten) {minutes++;}}// 如果还有新鲜橘子,返回-1;否则返回时间return fresh == 0 ? minutes : -1;}
};int main(){Solution solution;// 测试用例1: [[2,1,1],[1,1,0],[0,1,1]]// 预期输出: 4vector<vector<int>> grid1 = {{2,1,1},{1,1,0},{0,1,1}};cout << "测试用例1: " << solution.orangesRotting(grid1) << endl;// 测试用例2: [[2,1,1],[0,1,1],[1,0,1]]// 预期输出: -1vector<vector<int>> grid2 = {{2,1,1},{0,1,1},{1,0,1}};cout << "测试用例2: " << solution.orangesRotting(grid2) << endl;// 测试用例3: [[0,2]]// 预期输出: 0vector<vector<int>> grid3 = {{0,2}};cout << "测试用例3: " << solution.orangesRotting(grid3) << endl;// 测试用例4: [[1]]// 预期输出: -1vector<vector<int>> grid4 = {{1}};cout << "测试用例4: " << solution.orangesRotting(grid4) << endl;return 0;
}
//127 - 转化成图求最短路径即可,我这里一开始想的dijstra,交上去虽然过了,但是耗时比较多,因为这计算了每个节点的距离,所有还有一种BFS也在里面,时间要节省一些。#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <climits>
#include <queue>
using namespace std;class Solution {
private:int n; // 目标节点索引
public:bool similar(string a, string b) {int diff = 0;for (int i = 0; i < a.size(); i++) {if (a[i] != b[i]) {diff++;}}return diff == 1;}int dijstra(vector<vector<bool>>& grid, int target) {int size = grid.size();vector<int> dij(size, size + 1);vector<bool> check(size, false);dij[0] = 0;for (int count = 0; count < size; count++) {int minDist = INT_MAX;int u = -1;// 找到未访问节点中距离最小的节点for (int i = 0; i < size; i++) {if (!check[i] && dij[i] < minDist) {minDist = dij[i];u = i;}}if (u == -1) break; // 没有可达节点check[u] = true;// 更新相邻节点的距离for (int v = 0; v < size; v++) {if (!check[v] && grid[u][v] && dij[u] != INT_MAX) {if (dij[u] + 1 < dij[v]) {dij[v] = dij[u] + 1;}}}}return dij[target] == size + 1 ? -1 : dij[target];}int bfs(vector<vector<bool>>& grid, int target) {int size = grid.size();vector<int> dist(size, -1);queue<int> q;q.push(0);dist[0] = 0;while (!q.empty()) {int curr = q.front();q.pop();if (curr == target) {return dist[target];}// 遍历所有相邻节点for (int next = 0; next < size; next++) {if (grid[curr][next] && dist[next] == -1) {dist[next] = dist[curr] + 1;q.push(next);}}}return dist[target];}int ladderLength(string beginWord, string endWord,vector<string>& wordList, bool useBFS = true) {vector<vector<bool>> grid(wordList.size() + 2,vector<bool>(wordList.size() + 2, false));if (find(wordList.begin(), wordList.end(), endWord) == wordList.end()) {return 0;}for (int i = 0; i < wordList.size(); i++) {if (similar(beginWord, wordList[i])) {grid[0][i + 1] = true;}}for (int i = 0; i < wordList.size(); i++) {if (endWord == wordList[i]) {n = i + 1;}for (int j = i; j < wordList.size(); j++) {if (similar(wordList[j], wordList[i])) {grid[i + 1][j + 1] = true;grid[j + 1][i + 1] = true;}}}int result;if (useBFS) {cout << "使用BFS算法..." << endl;result = bfs(grid, n);} else {cout << "使用Dijkstra算法..." << endl;result = dijstra(grid, n);}return result == -1 ? 0 : result + 1;}
};int main() {Solution solution;cout << "请选择算法:" << endl;cout << "1. BFS (广度优先搜索)" << endl;cout << "2. Dijkstra (迪杰斯特拉算法)" << endl;cout << "请输入选择 (1 或 2): ";int choice;cin >> choice;bool useBFS = (choice == 1);cout << "\n开始测试..." << endl;// 测试用例1string beginWord1 = "hit";string endWord1 = "cog";vector<string> wordList1 = {"hot","dot","dog","lot","log","cog"};cout << "\n测试用例1: beginWord=\"hit\", endWord=\"cog\"" << endl;cout << "wordList=[\"hot\",\"dot\",\"dog\",\"lot\",\"log\",\"cog\"]" << endl;int result1 = solution.ladderLength(beginWord1, endWord1, wordList1, useBFS);cout << "结果: " << result1 << endl;// 测试用例2string beginWord2 = "hit";string endWord2 = "cog";vector<string> wordList2 = {"hot","dot","dog","lot","log"};cout << "\n测试用例2: beginWord=\"hit\", endWord=\"cog\"" << endl;cout << "wordList=[\"hot\",\"dot\",\"dog\",\"lot\",\"log\"]" << endl;int result2 = solution.ladderLength(beginWord2, endWord2, wordList2, useBFS);cout << "结果: " << result2 << endl;return 0;
}

练习建议:

  • 队列是核心:牢记BFS的核心数据结构是队列。queue.push() 对应将新节点加入下一层待访问列表,queue.pop() 对应取出当前层的节点进行处理。

  • 距离/步数 tracking: 通常需要一个dist数组或 map 来记录从起点到每个节点的最短距离。在BFS中,dist[neighbor] = dist[current] + 1 是更新距离的关键。对于“腐烂的橘子”,BFS的层数本身就代表了分钟数。

  • 避免重复访问:和DFS一样,visited 数组或 set 对于BFS至关重要,用于确保每个节点只入队一次,防止在有环的图中无限循环。


目标达成自查 (约 0.5 小时)

完成以上练习后,进行复盘和总结:

  1. DFS vs. BFS 的核心区别是什么?

    • 数据结构:DFS主要依赖递归(系统栈),BFS依赖队列。

    • 搜索顺序:DFS是“深度”优先,一路走到底;BFS是“广度”优先,一层层扩展。

  2. 如何根据问题选择算法?

    • 问题要求最短路径/最少步数/最少操作次数吗? -> 首选 BFS

    • 问题要求找出所有方案/组合/排列,或者只是判断“能不能”到达某个状态(不要求最短)? -> DFS 更自然

  3. 两种搜索算法的通用模板是什么?

    • 我能否独立、无误地写出DFS回溯和BFS层次遍历的基本代码框架?

    • 我是否清楚在模板的哪个位置进行“访问”标记(visited)可以避免重复计算?(对于BFS,在节点入队时标记;对于DFS,在刚进入递归函数时标记)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/98145.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/98145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android模拟简单的网络请求框架Retrofit实现

文章目录1.静态代理2.动态代理3.实现简单的Retrofit定义对应的请求注解参数通过动态代理模拟Retrofit的创建请求参数的处理定义请求接口测试请求1.静态代理 代理默认给某一个对象提供一个代理对象&#xff0c;并由代理对象控制对原对象的引用。通俗来讲&#xff0c;代理模式就…

Matter安全实现

Matter分析与安全验证 上一篇文章简单的介绍了Matter的架构、实现、以及部分安全验证过程&#xff1b;这里继续补充一下Matter的其他安全验证流程&#xff0c;以更好的实现Matter安全。 Matter提供的安全实现流程大概总结起来是这个流程 硬件信任根→安全启动→动态证书→加密…

从基础到实践:Web核心概念与Nginx入门全解析

从基础到实践&#xff1a;Web核心概念与Nginx入门全解析 文章目录从基础到实践&#xff1a;Web核心概念与Nginx入门全解析一、Web是什么&#xff1f;从基本概念到核心架构1.1 Web的本质&#xff1a;一个超文本信息系统1.2 B/S架构&#xff1a;Web的“前端-后端”分工模式二、一…

【完整源码+数据集+部署教程】加工操作安全手套与手部检测系统源码和数据集:改进yolo11-cls

背景意义 研究背景与意义 随着工业自动化和智能制造的迅速发展&#xff0c;工人安全问题日益受到重视。特别是在涉及重型机械和危险操作的工作环境中&#xff0c;工人手部的安全保护显得尤为重要。传统的安全手套虽然在一定程度上能够保护工人的手部&#xff0c;但在复杂的加工…

代码随想录算法训练营第一天 || (双指针)27.移除元素 26.删除有序数组中的重复项 283.移动零 977.有序数组的平方

代码随想录算法训练营第一天 || (双指针)27.移除元素 26.删除有序数组中的重复项 283.移动零 27.移除元素 暴力方法 同向双指针双指针 自己AC的解答 卡哥的讲解 26.删除有序数组中的重复项 同向双指针 283.移动零 自己解答 灵神做法(同向双指针+交换) 977.有序数组的平方 暴…

Java全栈开发工程师面试实录:从基础到实战的深度探讨

Java全栈开发工程师面试实录&#xff1a;从基础到实战的深度探讨 一、初识与自我介绍 面试官&#xff08;李工&#xff09;&#xff1a; 你好&#xff0c;欢迎来到我们公司。我是负责技术面试的李工&#xff0c;今天我们将进行一场关于Java全栈开发的深入交流。你可以先简单介绍…

Kafka:Java开发的消息神器,你真的懂了吗?

Kafka&#xff1a;Java开发的消息神器&#xff0c;你真的懂了吗&#xff1f; 一、Kafka 是什么鬼&#xff1f; 想象一下&#xff0c;你在网上疯狂剁手后&#xff0c;满心期待着快递包裹的到来。这时候&#xff0c;快递站就像是 Kafka&#xff0c;而你的包裹就是消息。快递站接…

深度学习之第八课迁移学习(残差网络ResNet)

目录 简介 一、迁移学习 1.什么是迁移学习 2. 迁移学习的步骤 二、残差网络ResNet 1.了解ResNet 2.ResNet网络---残差结构 三、代码分析 1. 导入必要的库 2. 模型准备&#xff08;迁移学习&#xff09; 3. 数据预处理 4. 自定义数据集类 5. 数据加载器 6. 设备配置…

Pinia 两种写法全解析:Options Store vs Setup Store(含实践与场景对比)

目标&#xff1a;把 Pinia 的两种写法讲透&#xff0c;写明“怎么写、怎么用、怎么选、各自优缺点与典型场景”。全文配完整代码与注意事项&#xff0c;可直接当团队规范参考。一、背景与准备 适用版本&#xff1a;Vue 3 Pinia 2.x安装与初始化&#xff1a; # 安装 npm i pini…

setup函数相关【3】

目录1.setup函数&#xff1a;1.概述&#xff1a;2.案例分析&#xff1a;2.setup函数的优化&#xff1a;&#xff08;setup语法糖&#xff09;优化1&#xff1a;优化2&#xff1a;安装插件&#xff1a;安装指令&#xff1a;只对当前项目安装配置vite.config.ts&#xff1a;代码编…

如何通过AI进行数据资产梳理

最终产出 数据资产清单 包含所有数据资产的详细目录,列出数据集名称、描述、所有者、格式、存储位置和元数据。 用途:帮助政府部门清晰了解数据资产分布和状态。 数据质量报告 数据质量评估结果,记录准确性、完整性、一致性等问题及改进建议,基于政府认可的数据质量框架(如…

【传奇开心果系列】Flet框架结合pillow实现的英文文字倒映特效自定义模板特色和实现原理深度解析

Flet框架结合pillow实现的英文文字倒映特效自定义模板特色和实现原理深度解析 一、效果展示截图 二、使用场景 三、特色说明 四、概括说明 五、依赖文件列表 六、安装依赖命令 七、 项目结构建议 八、注意事项 九、Flet 文字倒影效果实现原理分析 (一)组件结构与功能 1. 图像…

2025最新深度学习面试必问100题--理论+框架+原理+实践 (下篇)

2025最新深度学习面试必问100题–理论框架原理实践 (下篇) 在上篇中&#xff0c;我们已经深入探讨了机器学习基础、CNN、RNN及其变体&#xff0c;以及模型优化的核心技巧。 在下篇中&#xff0c;我们将把目光投向更远方&#xff0c;聚焦于当今AI领域最炙手可热的前沿。我们将深…

原子工程用AC6编译不过问题

…\Output\atk_h750.axf: Error: L6636E: Pre-processor step failed for ‘…\User\SCRIPT\qspi_code.scf.scf’修改前&#xff1a; #! armcc -E ;#! armclang -E --targetarm-arm-none-eabi -mcpucortex-m7 -xc /* 使用说明 ! armclang -E --targetarm-arm-none-eabi -mcpuco…

Python有哪些经典的常用库?(第一期)

目录 1、NumPy (数值计算基础库) 核心特点&#xff1a; 应用场景&#xff1a; 代码示例&#xff1a; 2、Pandas (数据分析处理库) 应用场景&#xff1a; 代码示例&#xff1a; 3、Scikit-learn (机器学习库) 核心特点&#xff1a; 应用场景&#xff1a; 代码示例&am…

现代 C++ 高性能程序驱动器架构

&#x1f9e0; 现代 C 高性能程序驱动器架构M/PA&#xff08;多进程&#xff09;是隔离的“孤岛”&#xff0c;M/TA&#xff08;多线程&#xff09;是共享的“战场”&#xff0c;EDSM&#xff08;事件驱动&#xff09;是高效的“反应堆”&#xff0c;MDSM&#xff08;消息驱动&…

投资储能项目能赚多少钱?小程序帮你测算

为解决电网负荷平衡、提升新能源消纳等问题&#xff0c;储能项目的投资开发越来越多。那么&#xff0c;投资储能项目到底能赚多少钱&#xff1f;适不适合投资&#xff1f;用“绿虫零碳助手”3秒钟精准测算。操作只需四步&#xff0c;简单易懂&#xff1a;1.快速登录&#xff1a…

Mac 能够连Wife,但是不能上网问题解决

请按照以下步骤从最简单、最可能的原因开始尝试&#xff1a; 第一步&#xff1a;基础快速排查 这些步骤能解决大部分临时性的小故障。 重启设备&#xff1a;关闭您的 Mac 和路由器&#xff0c;等待一分钟后再重新打开。这是解决网络问题最有效的“万能药”。检查其他设备&am…

基于SpringBoot的旅游管理系统的设计与实现(代码+数据库+LW)

摘要 本文阐述了一款基于SpringBoot框架的旅游管理系统设计与实现。该系统整合了用户信息管理、旅游资源展示、订单处理流程及安全保障机制等核心功能&#xff0c;专为提升旅游行业的服务质量和运营效率而设计。 系统采用前后端分离架构&#xff0c;前端界面设计注重跨设备兼…

Springboot乐家流浪猫管理系统16lxw(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表项目功能&#xff1a;领养人,流浪猫,领养申请开题报告内容基于Spring Boot的乐家流浪猫管理系统开题报告一、研究背景与意义随着城市化进程加速和人口增长&#xff0c;流浪猫问题已成为全球性社会挑战。据统计&#xff0c;全球每年约有1.5亿只无家可归的宠物&a…