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

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

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

一、图的遍历

图的遍历是指从图中的某个顶点出发,按照一定规则访问图中所有顶点且每个顶点仅访问一次的过程,核心分为深度优先搜索(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.普通求法#include<iostream> using namespace std; int main(){ int sum=1;...

    01背包问题

    问题定义01背包问题是一个经典的组合优化问题,通常描述如下:有个容量为C的背包有n件物品,第i件物品的重量为Wi,价值为Vi每种物品只有一件,可以选择放入背包(1)或不放入背包(0),因此称为“01”...

    树的存储与遍历—顺序存储

    顺序存储使用数组来存储二叉树节点,通过数组下标表示节点间的父子关系,一般适用于完全二叉树。1.存储规则根节点存储在索引 0 位置对于索引为 i 的节点:左子节点索引:2*i + 1右子节点索引:2*i...

    树的存储与遍历—链式存储

    一、定义链式存储是表示树结构最直观、最常用的一种方法。它的核心思想是:用链表中的节点来表示树中的每个元素。每个节点不仅包含数据本身,还包含指向其子节点的指针。二、基本结构对于一个普通的树(不一定是二叉...

    第十四届全国青少年信息学奥林匹克联赛初赛试题(NOIP2008年普及组初赛C++试题及参考答案)

    第十四届全国青少年信息学奥林匹克联赛初赛试题(NOIP2008年普及组初赛C++试题及参考答案)

    第十四届全国青少年信息学奥林匹克联赛初赛试题             ...