c++图论(三)之图的遍历

发布于:2025-03-17 ⋅ 阅读:(24) ⋅ 点赞:(0)

在 C++ 中实现图的遍历主要有两种经典算法:深度优先搜索(DFS)广度优先搜索(BFS)。以下是两种遍历方法的实现原理、代码示例及对比分析:


一、深度优先搜索(DFS)

核心思想
  • 递归或栈实现:从起点出发,尽可能深入访问未探索的邻接顶点,回溯后继续搜索。
  • 应用场景:路径查找、连通性检测、拓扑排序、回溯问题等。
C++ 实现(邻接表存储,递归与非递归版本)
#include <iostream>
#include <vector>
#include <stack>
using namespace std;

// 邻接表表示的图(无权图)
vector<vector<int>> adjList;

// 递归版 DFS
void dfsRecursive(int u, vector<bool>& visited) {
    visited[u] = true;
    cout << u << " ";  // 访问顶点
    for (int v : adjList[u]) {
        if (!visited[v]) {
            dfsRecursive(v, visited);
        }
    }
}

// 非递归版 DFS(栈实现)
void dfsIterative(int start) {
    vector<bool> visited(adjList.size(), false);
    stack<int> s;
    s.push(start);
    visited[start] = true;

    while (!s.empty()) {
        int u = s.top();
        s.pop();
        cout << u << " ";  // 访问顶点

        // 逆序入栈以保证顺序与递归一致
        for (auto it = adjList[u].rbegin(); it != adjList[u].rend(); ++it) {
            int v = *it;
            if (!visited[v]) {
                visited[v] = true;
                s.push(v);
            }
        }
    }
}

int main() {
    // 示例图的邻接表(5 个顶点)
    adjList = {
        {1, 4},    // 顶点 0 的邻居
        {0, 2, 3}, // 顶点 1 的邻居
        {1, 3},    // 顶点 2 的邻居
        {1, 2},    // 顶点 3 的邻居
        {0}        // 顶点 4 的邻居
    };

    vector<bool> visited(adjList.size(), false);
    cout << "递归 DFS: ";
    dfsRecursive(0, visited);  // 输出: 0 1 2 3 4

    cout << "\n非递归 DFS: ";
    dfsIterative(0);           // 输出: 0 4 1 3 2
    return 0;
}

二、广度优先搜索(BFS)

核心思想
  • 队列实现:从起点出发,逐层访问所有邻接顶点。
  • 应用场景:最短路径(无权图)、社交网络层级分析、连通性检测等。
C++ 实现(邻接表存储)
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

// 邻接表表示的图(无权图)
vector<vector<int>> adjList;

void bfs(int start) {
    vector<bool> visited(adjList.size(), false);
    queue<int> q;
    q.push(start);
    visited[start] = true;

    while (!q.empty()) {
        int u = q.front();
        q.pop();
        cout << u << " ";  // 访问顶点

        for (int v : adjList[u]) {
            if (!visited[v]) {
                visited[v] = true;
                q.push(v);
            }
        }
    }
}

int main() {
    // 示例图的邻接表(同 DFS 示例)
    adjList = {
        {1, 4},    // 顶点 0 的邻居
        {0, 2, 3}, // 顶点 1 的邻居
        {1, 3},    // 顶点 2 的邻居
        {1, 2},    // 顶点 3 的邻居
        {0}        // 顶点 4 的邻居
    };

    cout << "BFS: ";
    bfs(0);  // 输出: 0 1 4 2 3
    return 0;
}

三、DFS 与 BFS 的对比

特性 DFS BFS
数据结构 栈(递归/非递归) 队列
空间复杂度 O(h)(h 为树的高度) O(w)(w 为树的宽度)
最短路径适用性 不保证找到最短路径 保证无权图的最短路径
遍历顺序 深度优先 广度优先
应用场景 连通性检测、回溯问题、拓扑排序 最短路径、层级分析、扩散类问题

四、遍历算法的扩展应用

1. 连通分量检测(DFS/BFS)
int countConnectedComponents() {
    int count = 0;
    vector<bool> visited(adjList.size(), false);
    for (int u = 0; u < adjList.size(); ++u) {
        if (!visited[u]) {
            dfsRecursive(u, visited);  // 或调用 BFS
            count++;
        }
    }
    return count;
}
2. 路径记录(BFS 最短路径)
vector<int> shortestPathBFS(int start, int end) {
    vector<bool> visited(adjList.size(), false);
    vector<int> parent(adjList.size(), -1);
    queue<int> q;
    q.push(start);
    visited[start] = true;

    while (!q.empty()) {
        int u = q.front();
        q.pop();
        if (u == end) break;
        for (int v : adjList[u]) {
            if (!visited[v]) {
                visited[v] = true;
                parent[v] = u;
                q.push(v);
            }
        }
    }

    // 回溯路径
    vector<int> path;
    for (int at = end; at != -1; at = parent[at]) {
        path.push_back(at);
    }
    reverse(path.begin(), path.end());
    return path;  // 若路径为空或起点未访问,则不可达
}

五、总结

  • DFS 适合深入探索路径或解决回溯问题,递归实现简洁但可能栈溢出,非递归实现更可控。
  • BFS 适合逐层扩散和最短路径问题,队列实现保证了层级顺序。
  • 实际应用:根据问题需求选择遍历方式,结合 邻接表邻接矩阵 优化性能。

等等!!如果上面的代码看不懂,就看看下面的模版

DFS:
vector<int> adj[101];
bool visited[101];
void dfs(int u) {
  if (visited[u]) {
    return;
  }
  visited[u] = true;
  // 这里处理 node u
  
  for (auto v: adj[u]) {
    dfs(v);
  }
}

BFS:
queue<int> q;
bool visited[N];
int distance[N];
// 从节点 x 开始搜索
visited[x] = true, distance[x] = 0;
q.push(x);

while (!q.empty()) {
  int u = q.front(); q.pop();

  // 处理节点 u
  for (auto v : adj[u]) {
    // 处理 u 的所有相邻节点
    if (visited[v]) continue;

    visited[v] = true, distance[v] = distance[u]+1;
    q.push(v);
  }
}

明白没?如果还没明白就去请教AI吧


在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到