【C++数据结构——图】图的遍历(头歌教学实验平台习题) 【合集】

简介: 本文介绍了图的遍历算法,包括深度优先遍历(DFS)和广度优先遍历(BFS)。深度优先遍历通过递归方式从起始节点深入探索图,适用于寻找路径、拓扑排序等场景;广度优先遍历则按层次逐层访问节点,适合无权图最短路径和网络爬虫等应用。文中提供了C++代码示例,演示了如何实现这两种遍历方法,并附有测试用例及结果,帮助读者理解和实践图的遍历算法。

 

目录😋

任务描述

相关知识

一、深度优先遍历

1. 定义

2. 工作原理

(1)初始状态

(2)递归探索

(3)回溯机制

3. 示例代码

4. 优势和应用场景

(1)优势

(2)应用场景

二、广度优先遍历

1. 定义

2. 工作原理

(1)初始化

(2)迭代过程

3. 示例代码

4. 优势和应用场景

(1)优势

(2)应用场景

三、带权有向图

测试说明

通关代码

测试结果


任务描述

本关任务:实现图的遍历

相关知识

为了完成本关任务,你需要掌握:

  1. 深度优先遍历
  2. 广度优先遍历

一、深度优先遍历

1. 定义

  • 深度优先遍历(Depth - First Search,简称 DFS)是一种用于遍历或搜索图(包括树,树是一种特殊的图)的算法。采用递归算法的深度优先遍历是指在遍历图的过程中,通过递归调用函数自身来实现对图中节点的深度优先访问。
  • 其基本思想是从给定的起始节点开始,沿着一条路径尽可能深地探索图,直到无法继续或者达到目标节点,然后回溯到前一个节点,继续探索其他未访问的分支路径。

2. 工作原理

(1)初始状态

  • 选择一个起始节点,将其标记为已访问,以避免重复访问。
  • 通常会使用一个数据结构(如数组、布尔向量等)来记录节点是否被访问过。

(2)递归探索

  • 对于起始节点的每一个未访问的邻接节点,递归地调用深度优先遍历函数。这意味着每次找到一个新的邻接节点,就将其视为新的起始节点,继续深入探索它的邻接节点。
  • 例如,在一个简单的图中,如果从节点 A 开始,A 有邻接节点 B 和 C。先访问 B,然后对于 B 的未访问邻接节点(假设为 D 和 E),又会分别以 D 和 E 为新的起点进行递归探索,直到所有从 B 可达的节点都被访问,然后回溯到 A,再去访问 C 及其可达节点。

(3)回溯机制

  • 当一个节点的所有邻接节点都被访问后,递归函数会返回上一层调用,这就是回溯过程。回溯到上一层后,继续探索上一层节点的其他未访问邻接节点。
  • 例如,在上述例子中,当完成对以 B 为起点的所有可达节点的访问后,就会回溯到 A,然后开始访问 C 及其可达节点。

3. 示例代码(以简单图的邻接表表示为例)

以下是一个简单的 C++ 代码示例,用于对一个图进行深度优先遍历:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Graph {
private:
    int V;
    vector<vector<int>> adj;
public:
    Graph(int V) {
        this->V = V;
        adj.resize(V);
    }
    void addEdge(int u, int v) {
        adj[u].push_back(v);
    }
    void DFS(int v, vector<bool>& visited) {
        visited[v] = true;
        cout << v << " ";
        for (int i : adj[v]) {
            if (!visited[i]) {
                DFS(i, visited);
            }
        }
    }
};
int main() {
    int V = 5;
    Graph g(V);
    g.addEdge(0, 1);
    g.addEdge(0, 2);
    g.addEdge(1, 3);
    g.addEdge(1, 4);
    vector<bool> visited(V, false);
    g.DFS(0, visited);
    return 0;
}
image.gif

在这个示例中:

  • Graph 类表示图,V 是顶点数量,adj 是邻接表,用于存储每个顶点的邻接顶点。
  • addEdge 函数用于向图中添加边,将一个顶点的邻接顶点添加到其对应的邻接表中。
  • DFS 函数实现了深度优先遍历。它接受一个顶点索引 v 和一个用于记录访问状态的向量 visited。首先将当前顶点标记为已访问,然后输出当前顶点,接着遍历当前顶点的所有邻接顶点。对于未访问的邻接顶点,递归调用 DFS 函数进行深度优先遍历。
  • main 函数中,创建了一个有 5 个顶点的图,添加了一些边,初始化了访问向量,然后从顶点 0 开始进行深度优先遍历。

4. 优势和应用场景

(1)优势

  • 代码简洁直观:对于熟悉递归思想的开发者来说,递归实现的深度优先遍历代码结构相对简单,易于理解和实现。
  • 自然地反映深度优先的思想:递归调用的过程很好地模拟了不断深入图的过程,符合深度优先遍历的逻辑。

(2)应用场景

  • 图的连通性判断:可以判断一个图是否是连通图,或者找出图中的连通分量。
  • 拓扑排序(对于有向无环图):在对有向无环图进行拓扑排序时,深度优先遍历是一种常用的基础算法。
  • 寻找路径:用于在图中寻找从一个节点到另一个节点的路径,如在迷宫求解等问题中,将迷宫看作一个图,使用深度优先遍历寻找从起点到终点的路径。

二、广度优先遍历

1. 定义

  • 广度优先遍历(Breadth - First Search,简称 BFS)是一种图的遍历算法。它从给定的起始顶点开始,按照与起始顶点的距离远近,一层一层地向外扩展遍历图中的顶点,直到图中的所有顶点都被访问。在遍历过程中,先访问距离起始顶点距离为 1 的顶点,再访问距离为 2 的顶点,以此类推。

2. 工作原理

(1)初始化

  • 首先,将起始顶点标记为已访问,并且将其放入一个队列(Queue)中。队列是一种先进先出(FIFO)的数据结构,这保证了在遍历过程中先访问先加入的顶点的邻接顶点。

(2)迭代过程

  • 当队列不为空时,取出队首顶点,访问该顶点,然后遍历这个顶点的所有邻接顶点。对于每个未被访问的邻接顶点,将其标记为已访问,并放入队列中。
  • 这样,每次从队列中取出的顶点都是按照距离起始顶点的层次顺序进行的,先处理离起始顶点近的顶点,再逐渐处理更远的顶点。
  • 例如,在一个简单的图中,从顶点 A 开始进行广度优先遍历。A 被放入队列,然后取出 A,访问 A 并将 A 的邻接顶点 B、C 放入队列;接着取出 B,访问 B 并将 B 的邻接顶点 D、E 放入队列(如果 D、E 未被访问),以此类推,直到队列为空。

3. 示例代码(以简单图的邻接表表示为例)

以下是一个简单的 C++ 代码示例,用于对一个图进行广度优先遍历:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
class Graph {
private:
    int V;
    vector<vector<int>> adj;
public:
    Graph(int V) {
        this->V = V;
        adj.resize(V);
    }
    void addEdge(int u, int v) {
        adj[u].push_back(v);
    }
    void BFS(int start) {
        vector<bool> visited(V, false);
        queue<int> q;
        visited[start] = true;
        q.push(start);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            cout << v << " ";
            for (int i : adj[v]) {
                if (!visited[i]) {
                    visited[i] = true;
                    q.push(i);
                }
            }
        }
    }
};
int main() {
    int V = 6;
    Graph g(V);
    g.addEdge(0, 1);
    g.addEdge(0, 2);
    g.addEdge(1, 3);
    g.addEdge(1, 4);
    g.addEdge(2, 4);
    g.addEdge(3, 5);
    g.addEdge(4, 5);
    g.BFS(0);
    return 0;
}
image.gif

在这个示例中:

  • Graph 类表示图,V 是顶点数量,adj 是邻接表,用于存储每个顶点的邻接顶点。
  • addEdge 函数用于向图中添加边,将一个顶点的邻接顶点添加到其对应的邻接表中。
  • BFS 函数实现了广度优先遍历。它接受一个起始顶点索引 start。首先创建一个用于记录访问状态的向量 visited 和一个队列 q,将起始顶点标记为已访问并放入队列。在循环中,只要队列不为空,就取出队首顶点,访问该顶点,然后遍历它的邻接顶点。对于未被访问的邻接顶点,标记为已访问并放入队列。
  • main 函数中,创建了一个有 6 个顶点的图,添加了一些边,然后从顶点 0 开始进行广度优先遍历。

4. 优势和应用场景

(1)优势

  • 找到最短路径:在无权图(边没有权重的图)中,广度优先遍历可以找到从起始顶点到其他顶点的最短路径长度。因为它是按照距离层次进行遍历的,第一次访问到某个顶点时的路径长度就是最短路径长度。
  • 均匀地搜索图:相比于深度优先遍历可能会沿着一条路径深入而忽略其他部分,广度优先遍历可以比较均匀地搜索图,对图的整体结构有更好的探索效果。

(2)应用场景

  • 最短路径问题(无权图):如在社交网络中寻找两个人之间的最短关系链,或者在迷宫中寻找从起点到终点的最短路径(将迷宫看作一个图)。
  • 网络爬虫:广度优先遍历可以用于网络爬虫中,从一个起始网页开始,一层一层地爬取链接页面,先爬取离起始网页近的链接,再逐渐向外扩展。
  • 连通分量计算:和深度优先遍历一样,也可以用于计算图的连通分量,不过广度优先遍历是按照层次来划分的。

三、带权有向图

带权有向图如下图所示,要求指定初始点,从初始点出发进行图的遍历。

image.gif


测试说明

平台会对你编写的代码进行测试:

测试输入:

0
image.gif

预期输出:

从顶点0开始的DFS:
  0  1  2  5  4  3
从顶点0开始的BFS:
  0  1  3  2  5  4
image.gif

【提示:输出遍历顶点的格式为 printf("%3d",v);】

开始你的任务吧,祝你成功!


通关代码

#include <iostream>  
#include <vector>  
#include <queue>  
#include <stack>  
#include <algorithm> // 用于sort  
using namespace std;  
class Graph {  
private:  
    int V; // 顶点数  
    vector<vector<int>> adj; // 邻接表  
public:  
    // 构造函数  
    Graph(int V) {  
        this->V = V;  
        adj.resize(V);  
    }  
    // 添加有向边  
    void addEdge(int u, int v) {  
        adj[u].push_back(v);  
    }  
    // 深度优先遍历  
    void DFS(int start) {  
        vector<bool> visited(V, false); // 访问标记  
        stack<int> s; // 使用栈实现DFS  
        s.push(start);  
        cout << "从顶点" << start << "开始的DFS:\n";  
        while (!s.empty()) {  
            int v = s.top();  
            s.pop();  
            if (!visited[v]) {  
                visited[v] = true;  
                printf("%3d", v); // 输出顶点  
            }  
            // 遍历邻接节点,逆序入栈  
            for (int i = adj[v].size() - 1; i >= 0; --i) {  
                int neighbor = adj[v][i];  
                if (!visited[neighbor]) {  
                    s.push(neighbor);  
                }  
            }  
        }  
        cout << endl;  
    }  
    // 广度优先遍历  
    void BFS(int start) {  
        vector<bool> visited(V, false); // 访问标记  
        queue<int> q; // 使用队列实现BFS  
        q.push(start);  
        visited[start] = true;  
        cout << "从顶点" << start << "开始的BFS:\n";  
        while (!q.empty()) {  
            int v = q.front();  
            q.pop();  
            printf("%3d", v); // 输出顶点  
            // 遍历邻接节点  
            for (int neighbor : adj[v]) {  
                if (!visited[neighbor]) {  
                    visited[neighbor] = true;  
                    q.push(neighbor);  
                }  
            }  
        }  
        cout << endl;  
    }  
    // 函数:排序邻接表  
    void sortAdjacencyList() {  
        for (int i = 0; i < V; ++i) {  
            sort(adj[i].begin(), adj[i].end()); // 对每个邻接链表进行排序  
        }  
    }  
};  
int main() {  
    int V = 6; // 顶点数量  
    Graph g(V);  
    // 添加边 (u, v)  
    g.addEdge(5, 4);
    g.addEdge(4, 3);
    g.addEdge(3, 2);
    g.addEdge(2, 0);    
    g.addEdge(0, 1);  
    g.addEdge(0, 3);  
    g.addEdge(1, 2);  
    g.addEdge(3, 5);  
    g.addEdge(2, 5);  
    g.addEdge(5, 0);  
    
    // 手动控制邻接表的顺序  
    g.sortAdjacencyList();  
    int startVertex;  
    cin >> startVertex;  
    // 进行深度优先遍历和广度优先遍历  
    g.DFS(startVertex);  
    g.BFS(startVertex);  
    return 0;  
}

image.gif


测试结果

image.gif

image.gif

目录
相关文章
|
15小时前
|
算法 C++
【C++数据结构——查找】二叉排序树(头歌实践教学平台习题)【合集】
【数据结构——查找】二叉排序树(头歌实践教学平台习题)【合集】 目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果: 任务描述 本关任务:实现二叉排序树的基本算法。 相关知识 为了完成本关任务,你需要掌握:二叉树的创建、查找和删除算法。具体如下: (1)由关键字序列(4,9,0,1,8,6,3,5,2,7)创建一棵二叉排序树bt并以括号表示法输出。 (2)判断bt是否为一棵二叉排序树。 (3)采用递归方法查找关键字为6的结点,并输出其查找路径。 (4)分别删除bt中关键
24 11
【C++数据结构——查找】二叉排序树(头歌实践教学平台习题)【合集】
|
17小时前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
12 2
|
14小时前
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
29 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
15小时前
|
存储 算法 C++
【C++数据结构——查找】二分查找(头歌实践教学平台习题)【合集】
二分查找的基本思想是:每次比较中间元素与目标元素的大小,如果中间元素等于目标元素,则查找成功;顺序表是线性表的一种存储方式,它用一组地址连续的存储单元依次存储线性表中的数据元素,使得逻辑上相邻的元素在物理存储位置上也相邻。第1次比较:查找范围R[0...10],比较元素R[5]:25。第1次比较:查找范围R[0...10],比较元素R[5]:25。第2次比较:查找范围R[0..4],比较元素R[2]:10。第3次比较:查找范围R[3...4],比较元素R[3]:15。,其中是顺序表中元素的个数。
68 44
【C++数据结构——查找】二分查找(头歌实践教学平台习题)【合集】
|
14小时前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
21 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
14小时前
|
存储 人工智能 算法
【C++数据结构——图】最短路径(头歌教学实验平台习题) 【合集】
任务描述 本关任务:编写一个程序,利用Dijkstra算法,实现带权有向图的最短路径。 相关知识 为了完成本关任务,你需要掌握:Dijkst本关任务:编写一个程序,利用Dijkstra算法,实现带权有向图的最短路径。为了完成本关任务,你需要掌握:Dijkstra算法。带权有向图:该图对应的二维数组如下所示:Dijkstra算法:Dijkstra算法是指给定一个带权有向图G与源点v,求从v到G中其他顶点的最短路径。Dijkstra算法的具体步骤如下:(1)初始时,S只包含源点,即S={v},v的距离为0。
31 15
|
14小时前
|
存储 算法 C++
【C++数据结构——图】图的邻接矩阵和邻接表的存储(头歌实践教学平台习题)【合集】
本任务要求编写程序实现图的邻接矩阵和邻接表的存储。需掌握带权有向图、图的邻接矩阵及邻接表的概念。邻接矩阵用于表示顶点间的连接关系,邻接表则通过链表结构存储图信息。测试输入为图的顶点数、边数及邻接矩阵,预期输出为Prim算法求解结果。通关代码提供了完整的C++实现,包括输入、构建和打印邻接矩阵与邻接表的功能。
21 10
|
14小时前
|
算法 C++
【C++数据结构——图】最小生成树(头歌实践教学平台习题) 【合集】
【数据结构——图】最小生成树(头歌实践教学平台习题)目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:【合集】任务描述 本关任务:编写一个程序求图的最小生成树。相关知识 为了完成本关任务,你需要掌握:1.建立邻接矩阵,2.Prim算法。建立邻接矩阵 上述带权无向图对应的二维数组,根据它建立邻接矩阵,如图1建立下列邻接矩阵。注意:INF表示无穷大,表示整数:32767 intA[MAXV][MAXV];Prim算法 普里姆(Prim)算法是一种构造性算法,从候选边中挑
21 10
|
14小时前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
23 10
|
14小时前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
24 12

热门文章

最新文章

下一篇
开通oss服务