深度优先遍历DFS(邻链表版本)

一、深度优先遍历DFS

1、定义

  • 深度优先遍历(Depth-First Search,简称 DFS)是一种用于遍历或搜索图(graph)和树(tree)数据结构的算法。其基本思想是尽可能深入图的路径,然后回溯,直到找到所有连接的节点。在此过程中,DFS 递归地访问每个未访问的相邻节点。
  • 适用场景
    在图论中,DFS 用于搜索图的连接组件、检测环、构建生成树、路径查找等。
    在树数据结构中,DFS 用于遍历整个树,通常用来实现先序、中序和后序遍历。
  • 如何工作
    DFS 的基本流程是:
    从一个起点节点开始。
    标记当前节点为已访问。
    对于每个相邻节点,如果未被访问,则递归调用 DFS。
    如果所有相邻节点都已访问,则回溯到上一个节点。
    1、代码详解
#include <iostream>
#include <vector>
#include <list>
using namespace std;
// 图的类
class Graph {
public:
    // 构造函数,numVertices 是顶点数
    Graph(int numVertices);
    // 向图中添加边
    void addEdge(int src, int dest);
    // 深度优先遍历
    void DFS(int startVertex);

private:
    int numVertices; // 顶点数
    vector<list<int>> adjLists; // 邻接表
    vector<bool> visited; // 标记已访问的顶点

    // 深度优先遍历的辅助函数
    void DFSUtil(int vertex);
};

// 构造函数,初始化顶点数和邻接表
Graph::Graph(int numVertices) {
    this->numVertices = numVertices;
    adjLists.resize(numVertices);
    visited.resize(numVertices, false);
}

// 添加边
void Graph::addEdge(int src, int dest) {
    adjLists[src].push_back(dest);
}

// 深度优先遍历
void Graph::DFS(int startVertex) {
    // 重置访问标记
    fill(visited.begin(), visited.end(), false);
    // 从指定的起点开始遍历
    DFSUtil(startVertex);
}

// 深度优先遍历的辅助函数
void Graph::DFSUtil(int vertex) {
    // 标记当前顶点为已访问
    visited[vertex] = true;
    cout << vertex << " "; // 打印顶点

    // 递归访问邻接表中的所有相邻顶点
    for (int adjVertex : adjLists[vertex]) {
        if (!visited[adjVertex]) {
            DFSUtil(adjVertex);
        }
    }
}

int main() {
    // 创建一个有 5 个顶点的图
    Graph g(5);

    // 添加一些边
    g.addEdge(0, 1);
    g.addEdge(0, 2);
    g.addEdge(1, 3);
    g.addEdge(1, 4);

    // 从顶点 0 开始深度优先遍历
    std::cout << "DFS starting from vertex 0: ";
    g.DFS(0);

    return 0;
}

这个 C++ 程序定义了一个用于表示图的类 Graph,并提供了向图中添加边的功能,以及对图进行深度优先遍历 (DFS) 的功能。我们详细分析下这个程序的各个部分:

Graph 类

  • 成员变量:

    • numVertices: 表示图中的顶点数量。
    • adjLists: 使用向量 (vector) 存储邻接表,每个顶点对应一个 list<int>,表示该顶点的所有相邻顶点。
    • visited: 一个布尔向量,表示各顶点是否已经被访问。
  • 构造函数 Graph(int numVertices):

    • 初始化图,设置顶点数量,并根据顶点数量调整 adjListsvisited 的大小。
  • 成员函数 addEdge(int src, int dest):

    • 在图中添加一条边,表示从 src 顶点到 dest 顶点的连接。实现方式是将 dest 添加到 adjLists[src] 中。
  • 成员函数 DFS(int startVertex):

    • 从指定的起点进行深度优先遍历。它首先重置 visited,然后调用辅助函数 DFSUtilstartVertex 开始遍历。
  • 辅助函数 DFSUtil(int vertex):

    • 递归地访问顶点及其邻接顶点,完成深度优先遍历。它会标记 vertex 为已访问,然后递归遍历所有与 vertex 相邻且尚未访问的顶点。

主函数 main

  • 创建了一个有 5 个顶点的图 Graph g(5)
  • 使用 addEdge 添加了几条边,构成一个简单的有向图结构。
  • 然后通过 DFS(0) 从顶点 0 开始进行深度优先遍历,并输出遍历的结果。

程序输出

输出结果应该是一个深度优先遍历序列,从指定的起点顶点开始,按照递归方式遍历图。给定的添加边的顺序,将从顶点 0 开始,沿着 0 → 1 → 3 → 4 的路径进行遍历,之后可能会访问顶点 2。

总结

这个程序是一个基本的深度优先遍历实现,展示了如何使用邻接表来表示图,并实现了递归 DFS。它可以用作学习图数据结构和递归遍历算法的示例。

std::vector<std::list> adjLists

表示了 Graph 类中的一个成员变量 adjLists,用于存储图的邻接表。让我们来解析这行代码的构成:

  • std::vectorvector 是 C++ 标准库中一个动态数组类型,它可以自动调整大小。使用 vector 允许根据顶点数量动态调整邻接表的大小。
  • std::listlist 是 C++ 标准库中的双向链表类型。用 list 表示邻接表的每个元素,这样可以方便地在图中添加或删除边。
  • std::vector<std::list<int>>:这是一个由 list<int> 组成的 vector。每个 list<int> 表示一个顶点的邻接表,其中包含该顶点相邻的所有顶点的索引。
  • adjLists:这是这个 vector 的变量名。通常,邻接表是用来存储每个顶点的邻接关系。

邻接表的作用

在图的表示中,邻接表是一种常见的方法。对于图的每个顶点,邻接表存储与该顶点相连的所有其他顶点。因此,邻接表的大小与顶点数相等,adjLists[i] 表示与顶点 i 相连的顶点列表。

这个成员变量的使用

在这个 Graph 类中,adjLists 用于存储图的边的连接关系。在 addEdge 函数中,当添加边时,将目标顶点添加到源顶点的邻接列表中,即 adjLists[src].push_back(dest)。这代表在图中创建了一条从 srcdest 的有向边。

在深度优先遍历(DFS)中,邻接表也被用来遍历从某个起始顶点出发的所有相邻顶点。

总结

std::vector<std::list<int>> adjLists 是一种表示图邻接关系的结构,用于管理图中顶点之间的连接。这种结构适用于稀疏图,因为它的空间效率较高,并且允许动态增加或删除边。

this->numVertices = numVertices

这行代码中的 this->numVertices = numVertices; 主要用于在类的构造函数中初始化类的成员变量 numVertices。具体解释如下:

  • this:这是 C++ 中一个特殊的指针,指向当前实例化的对象。在类的构造函数中,this 指向正在初始化的对象。
  • numVertices:这是 Graph 类的一个成员变量,用于存储图的顶点数量。
  • numVertices = numVertices:等号左边的 numVerticesGraph 类的成员变量,而右边的 numVertices 是传递给构造函数的参数。通过 this-> 来明确左边的 numVertices 是成员变量,而不是局部变量。

作用

在构造函数中,成员变量的初始化是非常重要的步骤。这行代码将传递给构造函数的 numVertices 参数值赋给 Graph 类的成员变量 numVertices,以便在其他地方使用。

用途

在这个图的例子中,这行代码用来设置图的顶点数,然后使用这个数值来创建邻接表和访问标记。这个初始化过程确保类的内部状态在对象创建时处于一致的、正确的状态。

为什么需要 this

如果没有 this,代码可能会因为变量名称冲突而产生歧义。在构造函数中,参数 numVertices 与类的成员变量 numVertices 具有相同的名称,通过使用 this->,可以明确地指示要访问的成员变量,避免歧义。

总结

this->numVertices = numVertices; 是将构造函数的参数值赋给类的成员变量,这在构造函数中是常见的做法,用于确保对象在创建时初始化正确的内部状态。

这行代码 adjLists.resize(numVertices); 用于调整 adjLists 这个向量的大小,使其包含 numVertices 个元素。在当前上下文中,adjLists 是用于表示图的邻接表的数据结构。我们可以从几个方面解释这行代码的含义和用途。

什么是邻接表

邻接表是图的表示方法之一。在这种表示方式中,图的每个顶点都有一个列表,列表中存储与该顶点相连的其他顶点。邻接表适合用于表示稀疏图,其中大部分顶点之间没有直接连接。

向量 adjLists

adjListsGraph 类中的一个成员变量,使用 std::vector<std::list<int>> 作为数据结构。这个向量中的每个元素都是一个 std::list<int>, 代表一个顶点的邻接列表。

resize 方法

  • resize(numVertices): 这是 std::vector 的方法,它将向量的大小调整为 numVertices 个元素。
  • 如果当前向量的大小小于 numVertices,则 resize 会扩展向量,创建新元素。若大于 numVertices,则会移除多余的元素。
  • 在本例中,adjLists.resize(numVertices) 确保 adjLists 有足够的空间来表示图的所有顶点。

为什么需要 resize

这行代码通常用于初始化阶段。在图的构造函数中,通过传递给构造函数的 numVertices 参数确定图的顶点数。因此,在构造函数中,必须确保 adjLists 的大小足够容纳所有顶点的邻接表。

总结

这行代码用于调整邻接表的大小,使其与图的顶点数相匹配。这是构造函数中常见的操作,确保数据结构在初始化时处于正确的状态。
adjLists[src].push_back(dest); 是一行用于在图结构中添加一条边的代码,它的具体含义和作用如下:

前提知识

在图的邻接表表示中,使用数据结构来表示图的边和顶点关系。std::vector<std::list<int>> adjLists 通常用于存储每个顶点的邻接关系。adjLists 是一个向量,其中每个元素都是一个 list<int>,表示与某个顶点相邻的所有顶点。

这行代码的解释

  • src:这是源顶点的索引,表示起点。
  • dest:这是目标顶点的索引,表示终点。
  • adjLists[src]:这是 src 顶点的邻接表。它是一个 list<int>,包含与 src 相邻的所有顶点。
  • push_back(dest):这是向 list<int> 中添加元素的方法。push_back 会将 dest 添加到 adjLists[src] 的末尾。

作用

这行代码用于在图中添加一条从 srcdest 的边。它会将 dest 添加到 src 顶点的邻接表中,表示 srcdest 之间有连接。

示例

// 添加一条从顶点 1 到顶点 3 的边
adjLists[1].push_back(3);

在这个示例中,adjLists[1].push_back(3) 表示在图中添加了一条从顶点 1 到顶点 3 的边。通过这种方式,邻接表反映了图的边和顶点的关系。

注意事项

  • 有向图和无向图:在有向图中,这表示从 srcdest 的连接。如果是无向图,则需要同时添加从 destsrc 的连接。
  • 重复边list<int> 允许重复元素,因此可能会出现多条从 srcdest 的边。
  • 图的遍历:在深度优先遍历、广度优先遍历等图算法中,邻接表是重要的输入数据结构。

总结

这行代码用于在图的邻接表中添加一条边,从源顶点到目标顶点。这种方式在图的构造、添加边、构建拓扑结构等操作中非常有用。通过邻接表,你可以快速查找与某个顶点相邻的所有顶点,从而实现各种图相关的算法和操作。

  • 18
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值