当前位置:首页 > C++知识 > 正文内容

图的访问与遍历-深度优先搜索

亿万年的星光4周前 (11-16)C++知识94

一、图的遍历

图的遍历是指从图中的某个顶点出发,按照一定规则访问图中所有顶点且每个顶点仅访问一次的过程,核心分为深度优先搜索(DFS) 和广度优先搜索(BFS) 两大类,适用于无向图、有向图等各类图结构,也是图论算法(如路径查找、连通性判断)的基础。


关键点:

  1. 连通图与非连通图
    • 连通图(无向图):从任意顶点出发,DFS/BFS 可访问所有顶点;

    • 非连通图:需遍历所有顶点,对未访问的顶点再次调用 DFS/BFS(统计连通分量)。

  2. 有向图的遍历
    • 邻接表仅添加 “有向边”(初始化图时注释无向图的双向添加代码);

    • DFS/BFS 逻辑不变,但访问顺序依赖边的方向(可能无法访问所有顶点,即使图连通)。

  3. 两种遍历的区别
    特性DFSBFS
    实现方式递归(或栈)队列
    访问顺序深度优先,跳跃性强广度优先,层层递进
    空间复杂度O (n)(递归栈 / 栈)O (n)(队列)
    适用场景路径查找、拓扑排序无权图最短路径

二、深度优先搜索(DFS)

1. 核心思想

“一条路走到黑,走不通就回头”:从起始顶点出发,优先访问当前顶点的未访问邻接顶点,递归或栈实现该过程,直到所有可达顶点都被访问。

2. 实现逻辑(递归版,最直观)

  • visited数组标记顶点是否被访问(初始全为 0,访问后设为 1);

  • 访问当前顶点,标记为已访问;

  • 遍历当前顶点的所有邻接顶点,若未访问则递归调用 DFS。


假设是无向联通图,比如下面这样:


参考代码

#include <iostream>
#include <vector>
#include <stack>  
#include <queue>   
#include <cstring> 
using namespace std;
const int MAX_VERTEX = 100;    // 最大顶点数
vector<int> adj[MAX_VERTEX];   // 邻接表
bool visited[MAX_VERTEX];      // 访问标记
int vertex_count;              // 顶点总数
//初始化 
void initGraph(int n) {
    vertex_count = n;
    for (int i = 0; i < n; ++i) 
		adj[i].clear();
    memset(visited, false, sizeof(visited));
}
//添加边 
void addEdge(int u, int v) {
    adj[u].push_back(v);
    adj[v].push_back(u);
}
//深度优先遍历 (从任意一个顶点开始皆可) 
void dfs(int start) {
    stack<int> st;
    st.push(start);
    visited[start] = true; //访问过的进行标记 
    cout << start << " ";
    while (!st.empty()) {
        int curr = st.top();
        bool has_unvisited = false;
        // 遍历邻接表
        for (int i = 0; i < adj[curr].size(); ++i) {
            int neighbor = adj[curr][i];
            if (!visited[neighbor]) {
                st.push(neighbor);
                visited[neighbor] = true;
                cout << neighbor << " "; //输出邻居 
                has_unvisited = true;
                break;
            }
        }
        if (!has_unvisited) st.pop();
    }
}

int main() {
    // 1. 初始化连通图(5个顶点:0-4)
    initGraph(5);
    // 2. 构建连通图(所有顶点互通)
    addEdge(0, 1);
    addEdge(0, 2);
    addEdge(1, 3);
    addEdge(1, 4);
    addEdge(2, 4);
    // 3. 直接调用DFS(起始顶点选0,连通图一次遍历全图)
    cout << "连通图DFS遍历结果:";
    dfs(0);
    cout << endl;
    memset(visited, false, sizeof(visited));
    // 3. 直接调用DFS(起始顶点选1,连通图一次遍历全图)
    cout << "连通图DFS遍历结果:";
    dfs(1);
    cout << endl;
    return 0;
}

输出是

连通图DFS遍历结果:0 1 3 4 2
连通图DFS遍历结果:1 0 2 4 3



假设是无向非联通图


参考代码:

#include <iostream>
#include <vector>
#include <stack>  
#include <cstring> 
using namespace std;
const int MAX_VERTEX = 100;    // 最大顶点数
vector<int> adj[MAX_VERTEX];   // 邻接表
bool visited[MAX_VERTEX];      // 访问标记
int vertex_count;              // 顶点总数
// 初始化图
void initGraph(int n) {
    vertex_count = n;
    for (int i = 0; i < n; ++i) 
        adj[i].clear();
    memset(visited, false, sizeof(visited));
}
// 添加无向边
void addEdge(int u, int v) {
    adj[u].push_back(v);
    adj[v].push_back(u);
}
// 深度优先遍历
void dfs(int start) {
    stack<int> st;
    st.push(start);
    visited[start] = true; // 访问过的进行标记 
    cout << start << " ";
    while (!st.empty()) {
        int curr = st.top();
        bool has_unvisited = false;
        // 遍历邻接表
        for (int i = 0; i < adj[curr].size(); ++i) {
            int neighbor = adj[curr][i];
            if (!visited[neighbor]) {
                st.push(neighbor);
                visited[neighbor] = true;
                cout << neighbor << " "; // 输出邻居 
                has_unvisited = true;
                break;
            }
        }
        if (!has_unvisited) st.pop();
    }
}
// 全图遍历函数(处理非连通图+孤点核心)
void traverseAll(void (*traverseFunc)(int)) {
    // 每次遍历前重置访问标记,避免残留
    memset(visited, false, sizeof(visited));
    // 遍历所有顶点,未访问的顶点作为新起点
    for (int i = 0; i < vertex_count; ++i) {
        if (!visited[i]) {
            traverseFunc(i); // 对每个连通分量/孤点执行DFS
        }
    }
    cout << endl; // 遍历完成后换行
}

int main() {
    // 1. 初始化图(6个顶点:0-5,其中5是孤点)
    initGraph(6);
    // 2. 构建含孤点的非连通图:
    // 连通分量1:0-1-2(互相连通)
    addEdge(0, 1);
    addEdge(0, 2);
    addEdge(1, 2);
    // 连通分量2:3-4(互相连通)
    addEdge(3, 4);
    // 顶点5:无任何边(孤点)

    // 3. 直接调用DFS(仅遍历起始顶点1所在的连通分量:0-1-2)
    cout << "仅遍历顶点1所在连通分量:";
    dfs(1);
    cout << endl;
    // 4. 调用traverseAll(遍历全图:包括连通分量+孤点)
    cout << "traverseAll遍历全图(含孤点):";
    traverseAll(dfs);
    return 0;
}


扫描二维码推送至手机访问。

版权声明:本文由青少年编程知识记录发布,如需转载请注明出处。

分享给朋友:

相关文章

一笔画问题

【题目描述】如果一个图存在一笔画,则一笔画的路径叫做欧拉路,如果最后又回到起点,那这个路径叫做欧拉回路。根据一笔画的两个定理,如果寻找欧拉回路,对任意一个点执行深度优先遍历;找欧拉路,则对一个奇点执行...

【数论】同余定理与同余方程

定义同余定理是数论中的一个重要概念。它的定义是这样的:给定一个整数m,如果两个整数a和b满足(a-b)能够被m整除,即(a-b)/m 得到一个整数,那么就成整数a和b对模m同余,记作a≡b(mod m...

C++中的宏

一、预处理和编译器    首先,预编译器就是在编译器之前运行,换句话说,预编译器根据程序员的指示,决定实际要编译的内容。预编译器编译指令都以 # 开头。例如:1...

树的存储结构

【方法1:数组】称为父亲表示法const int m=10;          ...

取模运算总结——数论

编程竞赛有相当一部分题目的结果过于庞大,整数类型无法存储,往往只要求输出取模的结果。例如(a+b)%p,若a+b的结果我们存储不了,再去取模,结果显然不对,我们为了防止溢出,可以先分别对a取模,b取模...

DEVC++如何支持C++11

DEVC++如何支持C++11

DEVC++默认开启C++11,需要手动添加C++11支持。DEVC++需要使用高一点的版本,DEVC++5.11下载地址:(1)  官方下载地址: Dev-C++ downloa...