当前位置:首页 > C++目录 > 正文内容

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

亿万年的星光2个月前 (11-16)C++目录249

一、图的遍历

图的遍历是指从图中的某个顶点出发,按照一定规则访问图中所有顶点且每个顶点仅访问一次的过程,核心分为深度优先搜索(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;
}


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

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

分享给朋友:

相关文章

质数(素数)的判断

一、定义法// 1 定义法(除了1和他本身之外,没有任何一个数能被整除)(试除法) bool is_prime3(unsigned long lon...

c++ 如何用链表存取数据

c++ 如何用链表存取数据

由于单链表的每个结点都有一个数据域和一个指针域。所以,每个结点可以定义成一个记录。其中,DATA数据元素,可以为你想要储存的任何数据格式,可以是数组,可以是int,甚至可以是结构体(这就是传说中的结构...

NOIP/CSP-J复赛历年考点

2000计算器的改良税收与补贴乘积最大单词接龙模拟、字符串模拟字符串、动态规划广度优先bfs、字符串2001数的计数最大公约数与最小公倍数求先序排列装箱问题模拟模拟、函数二叉树贪心2002级数求和选数...

C++ 如何隐藏光标

在C++控制台做小游戏的时候,光标一直在闪,影响体验效果,我们可以通过下面的函数隐藏光标位置。void HideCursor(){ CONSOLE_CURSOR_INFO cu...

CSP-J2021年普及组复赛T4——小熊的果篮

【题目描述】    小熊的水果店里摆放着一排 n 个水果。每个水果只可能是苹果或桔子,从左到右依 次用正整数 1、2、3、……、n 编号。连续排在一起的同一种...

树的遍历

在应用树结构解决问题时,往往要求按照某种此项获得树中全部结点的信息,这种操作叫做树的遍历。遍历的方法有很多种。常用的有:A. 先序遍历:先访问根结点,再从左到右按照先序思想遍历各子树。B. 后序遍历:...