🐭 DFS 深搜 & 🌀 BFS 广搜 通关站

给五年级的你 · 系统搞定 GESP 六级搜索算法

递归已会 ✓ 直接上手

0先看这里:搜索到底在搜什么?

深搜和广搜,是两个“在迷宫/地图/选择树里找答案”的方法。它们把每一步选择当作「岔路口」,一个接一个地试。

DFS 深搜:像一只勇敢的小老鼠,一条路走到底,撞墙了才退回来换一条路。适合「把所有方案都枚举出来」。

BFS 广搜:像往水面扔石头,波纹一圈一圈往外扩散。适合「找最近的/最短的」。

GESP 六级考纲原话:掌握深度优先搜索(DFS)、宽度优先搜索(BFS)的概念及应用,能根据现实问题选择合适的搜索算法。所以考试主要看三件事:

下面第 1 节先热身,把两个「秘密武器」栈和队列搞定,后面的动画就能看得明明白白。

1热身:栈 Stack 和队列 Queue

DFS 的「退回来」靠,BFS 的「一圈一圈」靠队列。先动手玩一玩!

🪵 栈 Stack —— 像叠盘子:后放上去的,先拿走(后进先出 LIFO)

栈:(空的)

盘子 压入 = 放在最上面;弹出 = 只能拿走最上面的。想拿最下面的盘子?不行,得一个个来。

🚌 队列 Queue —— 像排队买奶茶:先来的先买(先进先出 FIFO)

队列:(空的)

排队的人 入队 = 站到最后面;出队 = 最前面的人离开。BFS 的「一圈一圈扩散」就是按入队的先后一个个处理。

C++ 里怎么用?

#include <bits/stdc++.h>   // 比赛常用,一次包含所有头文件
using namespace std;

int main() {
    // ---- 栈 stack:后进先出 ----
    stack<int> s;
    s.push(1);   s.push(2);   s.push(3);
    int top = s.top();        // top = 3,只看不拿走
    s.pop();                  // 真正拿走 3
    s.empty();                // 空了吗?现在 false
    s.size();                 // 现在有几个?2 个

    // ---- 队列 queue:先进先出 ----
    queue<int> q;
    q.push(1);   q.push(2);   q.push(3);
    int front = q.front();    // front = 1,最前面的
    q.pop();                  // 拿走 1
    q.empty();                // false
    q.size();                 // 2 个

    // ---- 存坐标 (x,y):用 pair ----
    queue<pair<int,int>> q2;
    q2.push(make_pair(2, 3));
    int x = q2.front().first;    // x = 2
    int y = q2.front().second;   // y = 3
    return 0;
}

2DFS 深度优先搜索

思想一句话:往前走,能走就走;走不通,退回来换一条。(英文名 Depth-First Search,所以叫 DFS,也叫「深搜」)

2.1 模板:DFS 就是「递归 + 标记 + 回溯」

你有递归基础,那 DFS 只差两个小零件:① used/vis 标记防止重复走;② 回溯时撤销标记(有些题需要)。

void dfs(当前状态) {
    if (达到目标/边界) { 记录答案; return; }
    for (每一个可以做的选择) {
        if (这个选择被用过了) continue;   // ① 标记检查
        used[选择] = true;                // ① 标记
        做这个选择;
        dfs(下一步);                      // 递归往下走
        used[选择] = false;               // ② 回溯:撤销标记!
    }
}
⚠️ 最常犯的错:忘记 used[i] = false(回溯),导致后面的方案少了一大堆;或者忘记标记 used[i] = true,导致无限递归/爆栈。

2.2 🐭 动画:DFS 走迷宫(看栈怎么「进进出出」)

绿色是起点,红色是终点。深搜的策略是固定顺序「右 → 下 → 左 → 上」,一条道走到黑,撞墙就出栈回退。左边是栈里存的格子。

起点 终点 走过/访问过 走不通(出栈) 当前位置

注意看:深搜经常「东撞西撞」,找到的路径不一定最短!但它的好处是——只要有条路,就一定能找到,而且能顺便把所有走法都试一遍。

2.3 递归版代码(考试最爱考)

上面的动画是「手写栈」版(方便你看栈怎么动)。考试里直接用递归更省事,因为递归就是系统帮你用栈!

#include <bits/stdc++.h>
using namespace std;

int n, m;
char mp[105][105];        // 地图:'#'墙  '.'路
bool vis[105][105];
int dx[4] = {0, 1, 0, -1};   // 右 下 左 上
int dy[4] = {1, 0, -1, 0};

// 深搜:看看能不能从 (x,y) 走到终点
bool dfs(int x, int y) {
    if (x < 0 || x >= n || y < 0 || y >= m) return false;  // 越界
    if (mp[x][y] == '#') return false;                      // 撞墙
    if (vis[x][y]) return false;                            // 走过(别绕圈)
    vis[x][y] = true;                                       // 标记!
    if (mp[x][y] == 'T') return true;                       // 到终点啦

    for (int k = 0; k < 4; k++) {
        if (dfs(x + dx[k], y + dy[k])) return true;         // 只要有一步能到终点
    }
    return false;                                           // 四个方向都不行
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) cin >> mp[i];
    int sx, sy;                                             // 找起点 S
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            if (mp[i][j] == 'S') { sx = i; sy = j; }
    cout << (dfs(sx, sy) ? "YES" : "NO") << endl;
    return 0;
}
💡 注意:这个版本的 vis 标记不用撤销——因为我们的目的是「能不能到」,走过的地方再走一遍没意义。但是!在「枚举所有方案」的题里(比如全排列),回溯时必须 used[i] = false。后面例题会再强调。

2.4 🌳 动画:DFS 枚举全排列(看递归树怎么长)

把 1、2、3 排成所有可能的顺序(3! = 6 种)。DFS 会从「起点」一层层往下选数,选完一层就往深处走,走到底就输出一种排列,然后回溯换下一个数。这就是递归树!

// 全排列:把 1~n 的所有排列方式都输出(DFS 经典题)
#include <bits/stdc++.h>
using namespace std;

int n;
int a[15];                 // a[k] 存第 k 个位置放哪个数
bool used[15];             // used[i]:数字 i 用过了吗

void dfs(int k) {          // 准备填第 k 个位置
    if (k > n) {           // 填完了,输出这一种排列
        for (int i = 1; i <= n; i++) cout << a[i] << " ";
        cout << endl;
        return;
    }
    for (int i = 1; i <= n; i++) {
        if (used[i]) continue;    // 数字 i 已经用过了
        used[i] = true;           // 标记
        a[k] = i;
        dfs(k + 1);               // 递归填下一个位置
        used[i] = false;          // 回溯:撤销标记,让 i 给别的方案用
    }
}

int main() {
    cin >> n;
    dfs(1);
    return 0;
}
💡 时间复杂度:全排列一共 n! 种,所以是 O(n!)。n=8 是 40320 种,n=12 约 4.8 亿种——所以 GESP 题里用 DFS 枚举时,n 一般不超过 10~15。

3BFS 广度优先搜索

思想一句话:从起点开始,先处理离它 1 步的所有格子,再处理 2 步的……一层一层往外扩。(Breadth-First Search → BFS,也叫「广搜」)

3.1 模板:BFS = 队列 + 一层层记录距离

void bfs(起点) {
    queue<状态> q;
    q.push(起点);
    dist[起点] = 0;              // 起点距离是 0
    while (!q.empty()) {
        取出队头 now;
        for (每个邻居 nxt) {
            if (nxt 越界 / 撞墙 / 已访问) continue;
            dist[nxt] = dist[now] + 1;   // 层数 + 1
            q.push(nxt);                 // 入队,等会儿处理它
        }
    }
}
⚠️ 关键:BFS 是「第一次访问到终点时的距离就是最短距离」!因为波纹是同时一圈圈扩散的,谁先碰到终点,谁就是最近的。

3.2 🌀 动画:同一个迷宫,用 BFS 走一遍

看颜色!每个格子的颜色代表它离起点几「层」:蓝色离得近,越往外越暖色。队头先出队,把它的 4 个邻居入队,一层一层推进。

起点 终点 第 1 层(近) 中间层 第 18 层(远)

对比一下:BFS 走的路线更「整齐」,像波纹一样铺开,找到终点时走的步数一定是最少的

3.3 BFS 求最短路径(GESP 超高频)

// 迷宫最短路:从 S 到 T 最少走几步(BFS 经典题)
#include <bits/stdc++.h>
using namespace std;

int n, m;
char mp[105][105];        // '#':墙  '.':路  'S':起点  'T':终点
int step[105][105];       // step[x][y] = 到 (x,y) 的步数,-1 表示没到过
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
queue<pair<int,int>> q;

int main() {
    cin >> n >> m;
    int sx, sy, ex, ey;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++) {
            cin >> mp[i][j];
            if (mp[i][j] == 'S') { sx = i; sy = j; }
            if (mp[i][j] == 'T') { ex = i; ey = j; }
        }

    memset(step, -1, sizeof(step));   // 全部标记成"没到过"
    q.push(make_pair(sx, sy));
    step[sx][sy] = 0;                 // 起点 0 步

    while (!q.empty()) {
        int x = q.front().first;
        int y = q.front().second;
        q.pop();

        if (x == ex && y == ey) {     // 第一次到终点 = 最短!
            cout << step[x][y] << endl;
            return 0;
        }

        for (int k = 0; k < 4; k++) {
            int nx = x + dx[k], ny = y + dy[k];
            if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;  // 越界
            if (mp[nx][ny] == '#') continue;                       // 墙
            if (step[nx][ny] != -1) continue;                      // 已经走过了
            step[nx][ny] = step[x][y] + 1;                         // 层数 + 1
            q.push(make_pair(nx, ny));
        }
    }
    cout << -1 << endl;          // 队列空了还没到 = 走不到
    return 0;
}
💡 想输出路径?开一个 pre[x][y] 数组,每步记录「我是从哪个格子来的」,到终点后从终点倒着走回去,就得到完整路径(练 7-4 有答案)。

4DFS vs BFS:什么时候用哪个?

对比DFS 深搜BFS 广搜
就像小老鼠一条路走到底水面波纹一圈圈扩散
用的工具栈 stack(常写递归)队列 queue
擅长枚举所有方案(全排列、组合)最短步数/次数
找到答案时不一定最短第一次找到 = 最短
实现难度代码短,但回溯要小心代码固定,不容易错
空间栈深 = 路径长度队列里同时存整层的格子,可能更大

小测试:你来当裁判!点击选项看对不对

1️⃣ 求迷宫从入口到出口的最短步数,用哪个?

✔️ BFS!求「最短」是 BFS 的招牌本领——第一次碰到终点就是最短距离。

2️⃣ 输出数字 1~4 的所有排列方式,用哪个?

✔️ DFS!枚举「所有方案」是 DFS 的强项,配合回溯可以一层层穷举。

3️⃣ 数一数地图里有几个「连通块」(相邻的 1 算一块),用哪个?

✔️ 两个都行!连通块只看「能不能连到一起」,DFS 递归最顺手,BFS 用队列也行。

4️⃣ 想把二叉树一层一层从左到右输出(层序遍历),用哪个?

✔️ BFS!「一层一层」就是队列的活。DFS 对应的是前序/中序/后序遍历。

5️⃣ 只想判断迷宫能不能走到终点(不管最短),用哪个最省事?

✔️ DFS 更省事(代码短)。当然 BFS 也可以——但没必要「杀鸡用牛刀」。
📌 口诀:「最短、最少、最近」→ 找 BFS;「所有、全排列、方案数、判断能否」→ 找 DFS。

5图 & 二叉树上的搜索(六级必考)

迷宫是「图」的一种特例。真正的图(Graph)用点 + 边表示,存法最常用邻接表。看看在这张 6 个点的图上,DFS 和 BFS 分别怎么走——可以切换!

5.1 🕸️ 动画:图上的 DFS / BFS 遍历

深搜会沿着一条边「钻」到底再回头;广搜则从 A 出发,一层层把「朋友的朋友」都找到。

5.2 邻接表建图 + 遍历代码

#include <bits/stdc++.h>
using namespace std;

const int N = 100005;
vector<int> g[N];        // 邻接表:g[x] 存所有和 x 相连的点
bool vis[N];

// DFS 遍历(递归):访问一个点,再访问它没去过的邻居
void dfs(int x) {
    vis[x] = true;
    cout << x << " ";                       // 访问顺序
    for (int i = 0; i < (int)g[x].size(); i++) {
        int y = g[x][i];
        if (!vis[y]) dfs(y);
    }
}

// BFS 遍历:队列一层层走
void bfs(int s) {
    queue<int> q;
    q.push(s);
    vis[s] = true;
    while (!q.empty()) {
        int x = q.front(); q.pop();
        cout << x << " ";                   // 访问顺序
        for (int i = 0; i < (int)g[x].size(); i++) {
            int y = g[x][i];
            if (!vis[y]) {
                vis[y] = true;
                q.push(y);
            }
        }
    }
}

int main() {
    int n, m;                    // n 个点,m 条边
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        int u, v; cin >> u >> v;
        g[u].push_back(v);       // 无向图:两边都连
        g[v].push_back(u);
    }
    dfs(1);
    return 0;
}
⚠️ 注意:BFS 里 vis 标记要在入队时就置 true(不是出队时),否则同一个点会被加进队列好多次!

5.3 🌲 二叉树上的搜索(对应关系要背下来)

二叉树操作本质
前序遍历:根 → 左 → 右DFS(递归)
中序遍历:左 → 根 → 右DFS(递归)
后序遍历:左 → 右 → 根DFS(递归)
层序遍历:一层一层从左到右BFS(队列)
// 二叉树:用指针存左右孩子
struct Node {
    int val;
    Node *left, *right;
};

// 前序遍历(DFS)
void preorder(Node *t) {
    if (t == NULL) return;
    cout << t->val << " ";      // 先根
    preorder(t->left);           // 再左
    preorder(t->right);          // 后右
}

// 层序遍历(BFS):用队列!
void levelorder(Node *root) {
    queue<Node*> q;
    q.push(root);
    while (!q.empty()) {
        Node *t = q.front(); q.pop();
        cout << t->val << " ";
        if (t->left)  q.push(t->left);
        if (t->right) q.push(t->right);
    }
}

6例题精讲(GESP 六级风格)

例 1:全排列【DFS】⭐ 必背

题意:输入 n,按字典序输出 1~n 的所有排列,每行一个。

思路:dfs(k) 填第 k 位;used 标记哪个数用过;填满就输出;回溯时撤销标记。上面 2.4 节有完整代码,这里是另一个常见写法:

#include <bits/stdc++.h>
using namespace std;
int n, a[15]; bool used[15];

void dfs(int k) {
    if (k == n) {                       // 0~n-1 位都填好了
        for (int i = 0; i < n; i++) cout << a[i] << " ";
        cout << endl;
        return;
    }
    for (int i = 1; i <= n; i++) {
        if (used[i]) continue;
        used[i] = true;
        a[k] = i;
        dfs(k + 1);
        used[i] = false;                // 回溯!
    }
}
int main() { cin >> n; dfs(0); return 0; }

例 2:骑士游历·最少步数【BFS】⭐ 必背

题意:在 8×8 棋盘上,国际象棋的「马」(走日字)从 (x1,y1) 跳到 (x2,y2),最少几步?

思路:马有 8 种跳法,就是 8 个「方向」。BFS 从起点扩散,第一次到终点就是最少步数。

#include <bits/stdc++.h>
using namespace std;

int step[9][9];
int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1};
int dy[8] = {2, 1, -1, -2, -2, -1, 1, 2};
queue<pair<int,int>> q;

int main() {
    int x1, y1, x2, y2;
    cin >> x1 >> y1 >> x2 >> y2;

    memset(step, -1, sizeof(step));
    q.push(make_pair(x1, y1));
    step[x1][y1] = 0;

    while (!q.empty()) {
        int x = q.front().first, y = q.front().second;
        q.pop();
        if (x == x2 && y == y2) {
            cout << step[x][y] << endl;
            return 0;
        }
        for (int k = 0; k < 8; k++) {
            int nx = x + dx[k], ny = y + dy[k];
            if (nx < 1 || nx > 8 || ny < 1 || ny > 8) continue;
            if (step[nx][ny] != -1) continue;
            step[nx][ny] = step[x][y] + 1;
            q.push(make_pair(nx, ny));
        }
    }
    cout << -1 << endl;
    return 0;
}

例 3:细胞计数【DFS/BFS 连通块】⭐ 必背

题意:n×m 的地图,'1' 是细胞,上下左右相邻的 1 算同一个细胞。求共有多少个细胞?

思路:扫一遍地图,遇到没访问过的 1 就计数 +1,然后 DFS 把整块连通的一起「淹没」。

#include <bits/stdc++.h>
using namespace std;

int n, m, cnt;
char mp[105][105];
int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};

void dfs(int x, int y) {
    if (x < 0 || x >= n || y < 0 || y >= m) return;
    if (mp[x][y] != '1') return;      // 不是细胞 或 已经被淹没
    mp[x][y] = '0';                   // 淹没它,防止重复计数
    for (int k = 0; k < 4; k++) dfs(x + dx[k], y + dy[k]);
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) cin >> mp[i];
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            if (mp[i][j] == '1') { cnt++; dfs(i, j); }
    cout << cnt << endl;
    return 0;
}
💡 这个技巧叫 Flood Fill(洪水填充)——把整块区域「灌满水」标记掉,是连通块题的标准做法。

例 4:二叉树的遍历【DFS/BFS】

题意:给一棵二叉树(用数组存:1 号是根,2i 是左孩子,2i+1 是右孩子,-1 表示没有),输出前序遍历和层序遍历。

#include <bits/stdc++.h>
using namespace std;

int n;
int lc[1005], rc[1005];     // lc[i], rc[i]:i 的左右孩子

void preorder(int x) {      // 前序:根 左 右
    if (x == -1) return;
    cout << x << " ";
    preorder(lc[x]);
    preorder(rc[x]);
}

void levelorder(int root) { // 层序:BFS
    queue<int> q;
    q.push(root);
    while (!q.empty()) {
        int x = q.front(); q.pop();
        cout << x << " ";
        if (lc[x] != -1) q.push(lc[x]);
        if (rc[x] != -1) q.push(rc[x]);
    }
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> lc[i] >> rc[i];
    preorder(1);  cout << endl;
    levelorder(1); cout << endl;
    return 0;
}

7练习题(先自己做,再点开答案)

建议:每道题先想 ① 用 DFS 还是 BFS?② 需要标记吗?③ 需要回溯吗?然后再动手写。

7-1 组合【DFS】

从 1~5 里选 3 个数,输出所有组合(要求每个组合内部按从小到大排,组合之间按字典序)。例如 1 2 3、1 2 4……

提示:全排列的兄弟题。区别:组合不看顺序,「1 2 3」和「3 2 1」是同一种,所以每次从「上一个选的数 + 1」开始选,就不会重复。

📖 看答案
#include <bits/stdc++.h>
using namespace std;
int a[10];   // a[k] 存第 k 个选的数

void dfs(int k, int last) {   // 选第 k 个,只能从 last+1 开始
    if (k == 4) {
        for (int i = 1; i <= 3; i++) cout << a[i] << " ";
        cout << endl;
        return;
    }
    for (int i = last + 1; i <= 5; i++) {
        a[k] = i;
        dfs(k + 1, i);
    }
}
int main() { dfs(1, 0); return 0; }

7-2 迷宫可达性【DFS】

n×m 迷宫('#' 墙、'S' 起点、'T' 终点),判断能否从 S 走到 T。n,m ≤ 100。

提示:就是 2.3 节的代码,直接背下来用。判断能否 → DFS。

📖 看答案
#include <bits/stdc++.h>
using namespace std;
int n, m; char mp[105][105]; bool vis[105][105];
int dx[4] = {0,1,0,-1}, dy[4] = {1,0,-1,0};
bool dfs(int x, int y) {
    if (x<0||x>=n||y<0||y>=m) return false;
    if (mp[x][y]=='#') return false;
    if (vis[x][y]) return false;
    vis[x][y] = true;
    if (mp[x][y]=='T') return true;
    for (int k=0;k<4;k++) if (dfs(x+dx[k],y+dy[k])) return true;
    return false;
}
int main() {
    cin>>n>>m;
    int sx=0,sy=0;
    for (int i=0;i<n;i++) for (int j=0;j<m;j++){ cin>>mp[i][j]; if(mp[i][j]=='S'){sx=i;sy=j;} }
    cout<<(dfs(sx,sy)?"YES":"NO")<<endl;
    return 0;
}

7-3 池塘计数【DFS 连通块】

n×m 地图,'W' 是水,'.' 是地。八个方向相邻的水算同一个池塘。数一数有几个池塘。

提示:例 3 细胞计数的变体:方向从 4 个变成 8 个(多 4 个斜方向),别的都一样。

📖 看答案
#include <bits/stdc++.h>
using namespace std;
int n, m, cnt; char mp[105][105];
int dx[8] = {-1,-1,-1,0,0,1,1,1};
int dy[8] = {-1,0,1,-1,1,-1,0,1};
void dfs(int x, int y) {
    if (x<0||x>=n||y<0||y>=m) return;
    if (mp[x][y]!='W') return;
    mp[x][y]='.';
    for (int k=0;k<8;k++) dfs(x+dx[k],y+dy[k]);
}
int main() {
    cin>>n>>m;
    for (int i=0;i<n;i++) cin>>mp[i];
    for (int i=0;i<n;i++)
        for (int j=0;j<m;j++)
            if (mp[i][j]=='W'){ cnt++; dfs(i,j); }
    cout<<cnt<<endl;
    return 0;
}

7-4 最短路 + 输出路径【BFS】⭐ 进阶

输出从 S 到 T 的最短步数,并输出路径上经过的坐标(每行一个坐标)。

提示:pre[x][y] 记录「我是从哪个格子来的」。到终点后,用栈从终点倒着压回去,再弹出就是正序路径。

📖 看答案
#include <bits/stdc++.h>
using namespace std;
int n, m; char mp[105][105];
int step[105][105];
pair<int,int> pre[105][105];      // pre[x][y]:从哪个格子来的
int dx[4] = {0,1,0,-1}, dy[4] = {1,0,-1,0};
queue<pair<int,int>> q;

int main() {
    cin>>n>>m;
    int sx,sy,ex,ey;
    for (int i=0;i<n;i++) for (int j=0;j<m;j++){
        cin>>mp[i][j];
        if(mp[i][j]=='S'){sx=i;sy=j;}
        if(mp[i][j]=='T'){ex=i;ey=j;}
    }
    memset(step,-1,sizeof(step));
    q.push(make_pair(sx,sy)); step[sx][sy]=0;
    while(!q.empty()){
        int x=q.front().first, y=q.front().second; q.pop();
        if(x==ex&&y==ey) break;
        for(int k=0;k<4;k++){
            int nx=x+dx[k], ny=y+dy[k];
            if(nx<0||nx>=n||ny<0||ny>=m) continue;
            if(mp[nx][ny]=='#') continue;
            if(step[nx][ny]!=-1) continue;
            step[nx][ny]=step[x][y]+1;
            pre[nx][ny]=make_pair(x,y);   // 记住"来路"
            q.push(make_pair(nx,ny));
        }
    }
    if(step[ex][ey]==-1){ cout<<-1<<endl; return 0; }
    cout<<step[ex][ey]<<endl;

    // 用栈把终点倒推回起点,再倒序输出
    stack<pair<int,int>> path;
    int x=ex, y=ey;
    while(!(x==sx&&y==sy)){
        path.push(make_pair(x,y));
        int tx=pre[x][y].first, ty=pre[x][y].second;
        x=tx; y=ty;
    }
    path.push(make_pair(sx,sy));
    while(!path.empty()){
        cout<<path.top().first<<" "<<path.top().second<<endl;
        path.pop();
    }
    return 0;
}

7-5 八数码·最少移动次数【BFS 状态搜索】⭐ 挑战

3×3 棋盘有 1~8 八个数字和一个空格。每次可以把空格和上下左右相邻的数字交换。给一个初始局面,问移到「目标局面」最少要多少步?

提示:棋盘本身就是一个「状态」,BFS 在状态之间走!把 3×3 棋盘转成一个 9 位数(如 123456780)当状态,用 map<int,int>unordered_map 记步数。GESP 六级偶尔会这么考「状态搜索」。

📖 看答案(思路版)
// 思路:每个状态是一个 9 位数(0 表示空格)
// 用 unordered_map<int,int> dist 记"状态 - 步数"
// BFS:
//   取出队头状态 s,找到 0 的位置 p
//   0 和上下左右的数字交换,得到新状态 t
//   如果 dist 里没有 t,dist[t] = dist[s]+1,入队
// 目标状态是 123456780,第一次出现就返回步数
// 注意:判断 0 能不能往上/下/左/右移,
//       要检查 p 在第几行第几列,别让 0 从第 2 行直接"穿"到第 4 列。

7-6 二叉树第 k 层的节点【BFS】

给一棵二叉树的先序遍历(用 -1 表示空子树),输出第 k 层有哪些节点(根是第 1 层),按从左到右。

提示:层序遍历时记录每个节点在第几层,dist 数组搞定。也可以用 DFS 带一个 depth 参数。

📖 看答案
#include <bits/stdc++.h>
using namespace std;
int lc[1005], rc[1005], dep[1005], k;

void bfs_level() {
    queue<int> q;
    q.push(1); dep[1] = 1;
    while (!q.empty()) {
        int x = q.front(); q.pop();
        if (dep[x] == k) cout << x << " ";
        if (lc[x] != -1) { dep[lc[x]] = dep[x] + 1; q.push(lc[x]); }
        if (rc[x] != -1) { dep[rc[x]] = dep[x] + 1; q.push(rc[x]); }
    }
}
// main 里:读入 n 个节点的左右孩子,读入 k,调用 bfs_level()

8考试小贴士(GESP 实战)

🎯 考场检查单(写完代码逐条对):① 方向数组 4 个(还是 8 个?) ② 越界判断 ③ 标记数组有没有置 true ④ 回溯有没有撤销 ⑤ 终点判断在哪 ⑥ 输出格式。