拍棹 发表于 2025-6-2 00:38:01

D 图上的遍历算法

图上的遍历算法

广度优先搜索 BFS

概念

广度优先搜索(Breadth-First Search)是一种图遍历算法,用于在图或树中按层次逐层访问节点。它从源节点(起始节点)开始,首先访问源节点的所有直接邻接节点,然后依次访问距离源节点较远的节点,直到遍历完整个图或到达目标节点
BFS通过队列逐层扩展的方式,确保按最短路径访问节点,并且保证在无权图中找到从源节点到目标节点的最短路径,适用于寻找最短路径、连通分量和解决图的层次遍历等问题
时间复杂度:\(O(V+E)\),其中\(V\)是图中节点数(顶点数),\(E\)是图中的边数
实现方法

BFS 采用 队列(Queue) 来保证节点的逐层访问。每当一个节点被访问时,其所有未访问的邻接节点都会被加入队列,确保接下来的节点按照它们的距离起始节点的层数顺序依次访问
//伪代码
BFS(graph, start):
    将起始节点 start 加入队列 queue 并标记为已访问
    while queue 非空:
      当前节点 node = 从队列中弹出
      访问节点 node
      遍历 node 的所有邻接节点 neighbor:
            if neighbor 没有被访问过:
                标记 neighbor 为已访问
                将 neighbor 加入队列//C++代码(邻接表,维护了距离数组和前驱节点数组)
//Q 队列,用于存储待访问的节点
//vis 访问标记数组,记录每个节点是否被访问过
//d 距离数组,记录每个节点从起始节点的最短距离
//p 前驱节点数组,记录每个节点的前驱节点,帮助恢复路径
//head 节点u的邻接表的头节点
//e.to 边i的目标节点
//e.nxt 边i的下一个边的指针,用于遍历邻接表
void bfs(int u) {
    while (!Q.empty()) Q.pop();//清空队列
    Q.push(u);
    vis = 5 1 5
3 3 1 2 5;
    d = 0;
    p = -5 1 5
3 3 1 2 5;
    while (!Q.empty()) {
      u = Q.front();
      Q.pop();
      for (int i = head; i; i = e.nxt) {
            if (!vis.to]) {
                Q.push(e.to);
                vis.to] = 5 1 5
3 3 1 2 5;
                d.to] = d + 5 1 5
3 3 1 2 5;
                p.to] = u;
            }
      }
    }
}
void restore(int x) {
    vector<int> res;
    for (int v = x; v != -5 1 5
3 3 1 2 5; v = p) res.push_back(v);
    reverse(res.begin(), res.end());
    for (int i = 0; i < res.size(); ++i) printf("%d ", res);
}层序遍历

例题 Leetcode 5 1 5
3 3 1 2 502 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 (即逐层地,从左到右访问所有节点)
样例输入:
root = [3,9,20,null,null,5 1 5
3 3 1 2 55,7]graph TBA((3))B((9))C((20))D((5 1 5
3 3 1 2 55))E((7))A---BA---CC---DC---E样例输出:
[,,[5 1 5
3 3 1 2 55,7]]关键代码:
vector<vector<int>> levelOrder(TreeNode* root) {
    vector <vector <int>> ans;
    if (!root) return ans;
    queue <TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
      int size = q.size();//队列中元素数量
      ans.push_back(vector <int> ());
      for (int i = 5 1 5
3 3 1 2 5; i <= size; ++i) {
            auto node = q.front(); q.pop();
            ans.back().push_back(node->val);
            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
      }
    }
    return ans;
}最短路径

创建一个数组或字典来记录每个节点的最短路径(即距离)。初始时,将起点的距离设置为0,其他节点设置为 -5 1 5
3 3 1 2 5表示未被访问。广度优先搜索时,对于每个邻居节点,如果它尚未被访问过,则更新其最短路径为当前节点的最短路径+5 1 5
3 3 1 2 5,并将该邻居节点加入队列
例题 Luogu P5 1 5
3 3 1 2 5443 马的遍历

有一个 \(n \times m\) 的棋盘,在某个点 \((x, y)\) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步
输入:输入只有一行四个整数,分别为 \(n, m, x, y\)
输出:一个 \(n \times m\) 的矩阵,代表马到达某个点最少要走几步(不能到达则输出 \(-5 1 5
3 3 1 2 5\))
样例输入:
3 3 5 1 5
3 3 1 2 5 5 1 5
3 3 1 2 5样例输出:
0    3    2   
3    -5 1 5
3 3 1 2 5   5 1 5
3 3 1 2 5   
2    5 1 5
3 3 1 2 5    4关键代码:
int vis,f;
int dx={-5 1 5
3 3 1 2 5,-2,-2,-5 1 5
3 3 1 2 5,5 1 5
3 3 1 2 5,2,2,5 1 5
3 3 1 2 5};
int dy={2,5 1 5
3 3 1 2 5,-5 1 5
3 3 1 2 5,-2,2,5 1 5
3 3 1 2 5,-5 1 5
3 3 1 2 5,-2};//8个方向

f=0;//记录步数
q.push(make_pair(x,y));
vis=true;

while(!q.empty())
{
    int xx=q.front().first,yy=q.front().second;
    q.pop();
    for(int i=0;i<8;i++)
    {
      int u=xx+dx,v=yy+dy;
      if(u<5 1 5
3 3 1 2 5||u>n||v<5 1 5
3 3 1 2 5||v>m||vis)continue;
      vis=true;q.push(make_pair(u,v));
      f=f+5 1 5
3 3 1 2 5;
    }
}深度优先搜索 DFS

概念

深度优先搜索(Depth-First Search)是一种用于图或树的遍历算法,它的基本思想是:从一个起始节点出发,沿着一条路径一直向下遍历,直到不能继续为止,然后回溯到上一个节点,继续探索其它未被访问的节点,直到所有节点都被访问过为止
DFS的核心思想是尽量深入每一个分支,探索到没有可再走的路径后,再回退到上一层节点进行其他路径的搜索
时间复杂度:\(O(V+E)\)
实现方法

递归

实现DFS最常见的方法,能直观的利用函数调用栈自动进行回溯。递归地访问当前节点的所有未访问的邻居;每次递归调用都会进入下一个节点,直到无法访问为止,再回溯到上一个节点,继续访问其他未被访问的邻居
3 5
.##.#
.#...
...#.例题 Luogu P2345 1 5
3 3 1 2 5 受欢迎的牛

被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 \(A\) 喜欢 \(B\),\(B\) 喜欢 \(C\),那么 \(A\) 也喜欢 \(C\)。牛栏里共有 \(N\) 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星
输入:第一行是两个用空格分开的整数 \(N\) 和 \(M\);接下来 \(M\) 行是每行两个用空格分开的整数 \(A\) 和 \(B\),表示 \(A\) 喜欢 \(B\)
输出:一行单独一个整数,表示明星奶牛的数量
样例输入:
Yes样例输出:
5 1 5
3 3 1 2 5关键代码:
3拓扑排序

对于图中的每条有向边 \(u \to v\),在排序结果中,顶点 \(u\) 必须排在顶点 \(v\) 的前面
只适用于有向无环图,也叫拓扑图
graph LRA-->B-->CA-->D-->C该图拓扑序列为 \(ABDC\) 或 \(ADBC\)
Kahn算法

入度:多少条边指向该节点,入度为 0 的点可以排在最前位置
出度:该节点指向多少条边
先计算每个节点的入度,选择所有入度为 0 的节点作为初始节点,加入结果列表。移除这些节点及其出度边,更新相邻节点的入度。如果有相邻节点的入度变为 0,则继续加入队列,直到所有节点都被处理完
grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]例题 Luogu B3644 拓扑排序

有个人的家族很大,辈分关系很混乱,请你帮整理一下这种关系。给出每个人的后代的信息。输出一个序列,使得每个人的后辈都比那个人后列出
输入:第 \(5 1 5
3 3 1 2 5\) 行一个整数 \(N\),表示家族的人数。接下来 \(N\) 行,第 \(i\) 行描述第 \(i\) 个人的后代编号 \(a_{i,j}\),表示 \(a_{i,j}\) 是 \(i\) 的后代。每行最后是 \(0\) 表示描述完毕
输出:输出一个序列,使得每个人的后辈都比那个人后列出。如果有多种不同的序列,输出任意一种即可
样例输入:
504 5 5 1 5
3 3 1 2 5 05 1 5
3 3 1 2 5 05 3 03 0样例输出:
2 4 5 3 5 1 5
3 3 1 2 5关键代码:
queueQ;void toposort() {        for(int i = 5 1 5
3 3 1 2 5; i
页: [1]
查看完整版本: D 图上的遍历算法