算法详解——图的深度优先遍历和广度优先遍历

目录

  • 一、图结构
  • 二、深度优先遍历
    • 2.1 图的遍历
    • 2.2 深度优先遍历过程
    • 2.3 深度优先遍历核心思想
    • 2.4 深度优先遍历实现
  • 三、广度优先遍历
    • 3.1 广度优先遍历过程
    • 3.2 广度优先遍历核心思想
    • 3.3 广度优先遍历实现
  • 参考文献

一、图结构

  图结构指的是如下图所示的由节点和边组成的数据。

在这里插入图片描述

二、深度优先遍历

2.1 图的遍历

  图的遍历任务指的是从图中某个节点开始,遍历得到图中所有节点的过程。

2.2 深度优先遍历过程

  假设我们从1号节点开始进行深度优先遍历。遍历的步骤如下:首先,我们选择1号顶点作为起始点。从1号顶点开始,我们尝试沿着边访问尚未到达过的顶点。我们发现2号顶点是未到达过的,所以我们移动到2号顶点。现在,以2号顶点为起点,我们继续尝试访问其他未到达过的顶点。这样,我们又到达了4号顶点。接着,以4号顶点为起点,我们尝试访问其他未到达过的顶点。然而,此时我们无法再通过4号顶点的边访问其他未到达过的顶点,所以我们需要回到2号顶点。回到2号顶点后,我们发现沿着2号顶点的边也无法再访问其他未到达过的顶点。因此,我们需要继续回到1号顶点。现在,我们继续检查1号顶点的边,看看是否还有其他未到达过的顶点。这时我们到达了3号顶点,然后以3号顶点为起点继续访问其他未到达过的顶点,最后到达了5号顶点。此时,所有顶点都已经被访问过,遍历结束。遍历的顺序如下图所示:

在这里插入图片描述

2.3 深度优先遍历核心思想

  深度优先遍历的核心思想在于:首先选择一个未被访问过的顶点作为起始点,然后沿着当前顶点的边前进到未被访问过的顶点。当当前顶点没有未访问过的邻居顶点时,则回溯到上一个顶点,继续试探访问其他顶点,直到所有的顶点都被访问过为止。显然,深度优先遍历是沿着图的某一条分支遍历直到末端,然后回溯,再沿着另一条分支进行同样的遍历,直到所有的顶点都被访问过为止。

2.4 深度优先遍历实现

  如何将这一过程用代码实现呢?在讨论代码实现之前,我们需要先解决如何存储一个图的问题。最常用的方法是使用一个二维数组 e e e 来表示,具体如下所示:

在这里插入图片描述

  上图中的二维数组中,第 i i i行第 j j j列表示从顶点 i i i到顶点 j j j是否存在一条边。其中, 1 1 1表示存在边, ∞ ∞ 表示不存在边。我们将自己到自己的路径(即 i i i等于 j j j)设为 0 0 0。这种图的存储方式被称为邻接矩阵存储法。

  注意观察的同学会发现,这个二维数组沿主对角线对称。这是因为上述图是无向图。无向图指的是图的边没有方向性,例如边1-5表示,1号顶点可以到达5号顶点,同时5号顶点也可以到达1号顶点。

  接下来,我们将解决如何使用深度优先搜索来实现图的遍历。

// C 代码

#include <stdio.h>

int book[101], sum, n, e[101][101];

void dfs(int cur) // cur 是当前所在的顶点编号
{
    int i;
    printf("%d ", cur);
    sum++; // 每访问一个顶点,sum就加1
    if (sum == n)
        return; // 所有的顶点都已经访问过则直接退出
    for (i = 1; i <= n; i++) // 从1号顶点到n号顶点依次尝试,看哪些顶点与当前顶点cur有边相连
    {
        // 判断当前顶点cur到顶点i是否有边,并判断顶点i是否已访问过
        if (e[cur][i] == 1 && book[i] == 0)
        {
            book[i] = 1; // 标记顶点i已经访问过
            dfs(i);      // 从顶点i再出发继续遍历
        }
    }
    return;
}

int main()
{
    int i, m, a, b;
    scanf("%d %d", &n, &m);
    // 初始化二维矩阵
    for (i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (i == j)
                e[i][j] = 0;
            else
                e[i][j] = 999999999; // 我们这里假设99999999为正无穷
        }
    }
    // 读入顶点之间的边
    for (i = 1; i <= m; i++)
    {
        scanf("%d %d", &a, &b);
        e[a][b] = 1;
        e[b][a] = 1; // 这里是无向图,所以需要将e [b] [a]也赋为1
    }
    // 从1号城市出发
    book[1] = 1; // 标记1号顶点已访问
    dfs(1);      // 从1号顶点开始遍历
    getchar();
    getchar();
    return 0;
# Python 代码

def dfs(cur):
    global sum
    print(cur, end=" ")
    sum += 1
    if sum == n:
        return
    for i in range(1, n + 1):
        if e[cur][i] == 1 and book[i] == 0:
            book[i] = 1
            dfs(i)

if __name__ == "__main__":
    book = [0] * 101
    sum = 0
    n, m = map(int, input().split())
    e = [[0] * 101 for _ in range(101)]
    for i in range(1, n + 1):
        for j in range(1, n + 1):
            if i == j:
                e[i][j] = 0
            else:
                e[i][j] = 999999999  # 我们这里假设99999999为正无穷
    for _ in range(m):
        a, b = map(int, input().split())
        e[a][b] = 1
        e[b][a] = 1  # 这里是无向图,所以需要将e [b] [a]也赋为1
    book[1] = 1
    dfs(1)

  在上面的代码中变量cur存储的是当前正在遍历的顶点,二维数组e存储的就是图的边(邻接矩阵),数组book用来记录哪些顶点已经访问过,变量sum用来记录已经访问过多少个顶点,变量n存储的是图的顶点的总个数。完整代码如下。

三、广度优先遍历

3.1 广度优先遍历过程

  广度优先遍历结果如下图所示:
在这里插入图片描述

  使用广度优先遍历这个图的过程如下:首先选择一个未被访问过的顶点作为起始顶点,例如以1号顶点为起点。将1号顶点放入队列中,然后将与1号顶点相邻的未访问过的顶点,即2号、3号和5号顶点依次放入队列中。如下图所示:

在这里插入图片描述

  接下来,将2号顶点相邻的未访问过的顶点4号顶点放入队列中。到此,所有顶点都被访问过,遍历结束。如下图所示:

在这里插入图片描述

3.2 广度优先遍历核心思想

  广度优先遍历的主要思想是:首先选择一个未被访问过的顶点作为起始顶点,然后访问其所有相邻的顶点。接着,对于每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束。

3.3 广度优先遍历实现

// C 代码

#include <stdio.h>

int main() {
    int i, j, n, m, a, b, cur, e[101][101], book[101] = {0}, que[10001], head, tail;

    scanf("%d %d", &n, &m);

    // 初始化二维矩阵
    for (i = 1; i <= n; i++) {
        for (j = 1; j <= n; j++) {
            if (i == j)
                e[i][j] = 0;
            else
                e[i][j] = 99999999; // 我们这里假设99999999为正无穷
        }
    }

    // 读入顶点之间的边
    for (i = 1; i <= m; i++) {
        scanf("%d %d", &a, &b);
        e[a][b] = 1;
        e[b][a] = 1; // 这里是无向图,所以需要将e[b] [a]也赋值为1
    }

    // 队列初始化
    head = 1;
    tail = 1;

    // 从1号顶点出发,将1号顶点加入队列
    que[tail] = 1;
    tail++;
    book[1] = 1; // 标记1号顶点已访问1

    // 当队列不为空的时候循环
    while (head < tail) {
        cur = que[head]; // 当前正在访问的顶点编号
        for (i = 1; i <= n; i++) { // 从1~n依次尝试
            // 判断从顶点cur到顶点i是否有边,并判断顶点i是否已经访问过
            if (e[cur][i] == 1 && book[i] == 0) {
                // 如果从顶点cur到顶点i有边,并且顶点i没有被访问过,则将顶点i入队
                que[tail] = i;
                tail++;
                book[i] = 1; // 标记顶点i已访问
            }
            // 如果tail大于n,则表明所有顶点都已经被访问过
            if (tail > n)
                break;
        }
        head++; // 当一个顶点扩展结束后,head++,然后才能继续往下扩展
    }

    for (i = 1; i < tail; i++)
        printf("%d ", que[i]);

    return 0;
}
# Python 代码

def main():
    n, m = map(int, input().split())
    e = [[0] * 101 for _ in range(101)]
    book = [0] * 101
    que = [0] * 10001
    head = tail = 1

    # 初始化二维矩阵
    for i in range(1, n + 1):
        for j in range(1, n + 1):
            if i == j:
                e[i][j] = 0
            else:
                e[i][j] = 99999999  # 我们这里假设99999999为正无穷

    # 读入顶点之间的边
    for _ in range(m):
        a, b = map(int, input().split())
        e[a][b] = 1
        e[b][a] = 1  # 这里是无向图,所以需要将e[b][a]也赋值为1

    # 从1号顶点出发,将1号顶点加入队列
    que[tail] = 1
    tail += 1
    book[1] = 1  # 标记1号顶点已访问

    # 当队列不为空的时候循环
    while head < tail:
        cur = que[head]  # 当前正在访问的顶点编号
        for i in range(1, n + 1):  # 从1~n依次尝试
            # 判断从顶点cur到顶点i是否有边,并判断顶点i是否已经访问过
            if e[cur][i] == 1 and book[i] == 0:
                # 如果从顶点cur到顶点i有边,并且顶点i没有被访问过,则将顶点i入队
                que[tail] = i
                tail += 1
                book[i] = 1  # 标记顶点i已访问
            # 如果tail大于n,则表明所有顶点都已经被访问过
            if tail > n:
                break
        head += 1  # 当一个顶点扩展结束后,head++,然后才能继续往下扩展

    for i in range(1, tail):
        print(que[i], end=" ")

if __name__ == "__main__":
    main()

参考文献

[1]啊哈磊. 2014《啊哈!算法》, 人民邮电出版社

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/456107.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣刷题Days18-190颠倒二进制位(js)

目录 1&#xff0c;题目 2&#xff0c;代码 1&#xff0c;逐位颠倒 800001011 循环过程&#xff1a; 最终结果&#xff1a; 3&#xff0c;学习与总结 1&#xff0c;<< 位运算符 1&#xff0c;题目 颠倒给定的 32 位无符号整数的二进制位。 2&#xff0c;代码 1…

高频:spring知识

1、bean的生命周期&#xff1f; 主要阶段 初始化 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext67424e82: startup date []; root of context hi…

代码原理文献阅读(4)_3.12

4.2.经济调度问题(ED) 事实上&#xff0c;UC本质上也是一种ED问题。但随着电力电子技术的快速发展&#xff0c;越来越多的新型设备接入电力系统&#xff0c;调度逐渐变得"杂"、"散"、"灵活" 。此时&#xff0c;系统受到不确定性的影响更加强烈&a…

微信小程序 uniapp+vue学生考勤签到系统19j29

签到基本是没一个学生和教育工作者都要面对的一个事情&#xff0c;传统的签到都是需要单独找老师签到&#xff0c;或者老师课堂上单名来完成的&#xff0c;但是随着时代的发展这种比较落后的管理方式已经逐渐的被广大高校摒弃&#xff0c;教育工作者需要一种更加灵活且操作方便…

OCR-free相关论文梳理

⚠️注意&#xff1a;暂未写完&#xff0c;持续更新中 引言 通用文档理解&#xff0c;是OCR任务的终极目标。现阶段的OCR各种垂类任务都是通用文档理解任务的子集。这感觉就像我们一下子做不到通用文档理解&#xff0c;退而求其次&#xff0c;先做各种垂类任务。 现阶段&…

Linux系统架构----Tomcat 部署

一.Tomcat概述 Tomcat服务器是一个免费的开放式源代码的web应用服务器&#xff0c;属于轻量级应用级服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发和调试JSP程序的首首选。 一般来说&#xff0c;tomcat虽然和Apache或者Nginx这些…

CSS:实现择色器透明度条的两种方法(附赠一个在线图片转base64网站)

一、效果展示 二、实现方式 1.锥形渐变 .main{width: 600px;height: 20px;background: repeating-conic-gradient(rgba(1, 1, 1, 0.1) 0 25%,transparent 0 50%);background-size: 20px 20px;} 2.背景图 将一个四方格图片转为base64然后直接在css中使用 .main1 {width: 600p…

RabbitMQ 模拟实现【四】:虚拟主机设计

文章目录 虚拟主机设计虚拟主机分析交换机和虚拟主机之间的从属关系核心 API发布消息订阅消息应答消息消费者管理类 虚拟主机设计 虚拟主机分析 类似于 MySQL 的 database&#xff0c;把交换机&#xff0c;队列&#xff0c;绑定&#xff0c;消息…进⾏逻辑上的隔离&#xff0…

医学数据分析中缺失值的处理方法

医学数据分析中缺失值的处理方法 &#xff08;为了更好的展示&#xff0c;在和鲸社区使用代码进行展示&#xff09; 医学数据分析中&#xff0c;缺失值是不可避免的问题。缺失值的存在会影响数据的完整性和准确性&#xff0c;进而影响分析结果的可靠性。因此&#xff0c;在进…

php+vue+mysql公司员工薪酬工资管理系统

采用面向对象的思维方式&#xff0c;以符合实际的功能与性能要求&#xff0c;并进行了创新。为了提升小型企业工资管理的自动化和友善性的小型企业工资管理系统。 本文提出了一种基于面向对象的思想方法&#xff0c;以适应系统的实际功能与性能要求。为了使小型企业工资管理更具…

柚见第十期(后端队伍接口详细设计)

创建队伍 用户可以 创建 一个队伍&#xff0c;设置队伍的人数、队伍名称&#xff08;标题&#xff09;、描述、超时时间 P0 队长、剩余的人数 聊天&#xff1f; 公开 或 private 或加密 信息流中不展示已过期的队伍 请求参数是否为空&#xff1f;是否登录&#xff0c;未登录不…

决策树 | 分类树回归树:算法逻辑

目录 一. 决策树(Decision Tree)1. 决策树的构建1.1 信息熵(Entropy)1.1.1 信息量&信息熵 定义1.1.2 高信息熵&低信息熵 定义1.1.3 信息熵 公式 1.2 信息增益(Information Gain)1.2.1 信息增益的计算1.2.2 小节 2. 小节2.1 算法分类2.2 决策树算法分割选择2.3 决策树算…

Python应用数值方法:工程与科学实践指南

信息技术时代的挑战与机遇 我们正处在一个信息技术高速发展的时代&#xff0c;这是一个科技与创新蓬勃发展的时代。大数据与人工智能的崛起&#xff0c;正以前所未有的速度推动着传统技术的智能化变革。这种变革不仅带来了前所未有的机遇&#xff0c;也对科学和工程技术人员的…

什么时候要分库分表

对于一个日活用户在百万数量级的商城来说&#xff0c;每天产生的订单数量可能在百万级&#xff0c;特别在一些活动促销期间&#xff0c;甚至上千万。 假设我们基于单表来实现&#xff0c;每天产生上百万的数据量&#xff0c;不到一个月的时间就要承受上亿的数据&#xff0c;这…

水库大坝安全监测中需要注意的事项

随着经济和社会的发展&#xff0c;水资源的需求也在不断增加。因此&#xff0c;建设水库已成为保障水资源的主要方式之一。然而&#xff0c;随着水库规模的增大和工程的复杂性的增加&#xff0c;水库大坝的安全问题也日益引起重视。为此&#xff0c;需要对水库大坝进行安全监测…

2024年云服务器ECS价格表出炉——阿里云

2024年阿里云服务器租用费用&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元&#xff0c;ECS u1服务器2核4G5M固定带宽199元一年&#xff0c;2核4G4M带宽轻量服务器一年165元12个月&#xff0c;2核4G服务…

变量的本质和命名规则

变量的本质 内存:计算机中存储数据的地方&#xff0c;相当于一个空间变量本质:是程序在内存中申请的一块用来存放数据的小空间 变量命名规则与规范 规则: 不能用关键字 关键字:有特殊含义的字符&#xff0c;JavaScript 内置的一些英语词汇。例如:let、var、if、for等>只…

2024阿里技术官重磅推出“Java进阶必备宝典” 5大专题 6000字解析

5.JVM实战 CPU占用过高案例实战 内存占用过高案例实战 15种方式编写高效优雅Java程序实战 6.JVM底层技术 亿级流量高井发下GC预估与调优 JHSDB工具透视L ambda底层实现 JVM(HotSpot)核心源码解读 JVM核心模块(GC算法)手写实战 核心三&#xff1a;网络编程与高效IO 1.网络…

人形双臂机器人重大进展!顶刊公布业界首个双臂通用协同操作架构

图1&#xff1a;人居环境下的人形双臂机器人系统 通用人形机器人作为近年来机器人与AI交叉领域的研究热点和技术竞争高地&#xff0c;因其具备在非结构化人居环境中承担各种琐碎家务的潜力而得到广泛关注。人形双臂系统直接承载着人形机器人操作任务的执行能力&#xff0c;通用…

使用ai智能工具,让短视频超强变现。利用人工智能创作短视频

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 以下文章简单介绍如何利用人工智能来制作短视频&#xff0c;来实现资源变现。 一、…