图论与算法(5)图的广度优先遍历应用

news2024/11/25 4:45:22

1. 广度优先遍历

1.1 树的广度优先遍历

树的广度优先遍历(Breadth-First Traversal),也称为层次遍历,是一种按层次顺序逐级访问树节点的遍历方式。在广度优先遍历中,先访问树的根节点,然后按照从上到下、从左到右的顺序逐层访问树的节点。

首先将树的根节点入队列,然后循环执行以下操作:出队列一个节点,对该节点进行处理,然后将该节点的所有子节点按顺序入队列。通过不断出队列和入队列的操作,可以按照层次顺序逐级遍历树的节点,直到队列为空。

广度优先遍历保证了在访问某一层节点之前,先访问上一层的所有节点。这种遍历方式通常适用于需要按层次分析树结构的情况,比如求解最短路径、最小生成树等问题。

值得注意的是,广度优先遍历仅适用于无向树或有向无环图。对于有向有环图,由于存在环路,可能导致遍历陷入死循环。

1.2 图的广度优先遍历

图的广度优先遍历(Breadth-First Traversal),也称为宽度优先搜索(BFS),是一种遍历图的算法。在广度优先遍历中,从图中的某个起始节点开始,逐层遍历图的节点,先访问当前节点的所有邻接节点,然后再按顺序访问邻接节点的邻接节点,以此类推,直到遍历完图中所有可达节点。

下面是图的广度优先遍历的代码:

package BFS;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * 图的广度优先遍历(BFS)
 * @author wushaopei
 * @create 2023-06-05 10:04
 */
public class GraphBFS {

    private Graph G;                      // 图对象
    private boolean[] visited;            // 记录顶点是否访问过
    private List<Integer> order;          // 记录顶点的遍历顺序

    public GraphBFS(Graph G) {
        this.G = G;
        visited = new boolean[G.V()];
        order = new ArrayList<>(G.V());

        for (int v = 0; v < G.V(); v++) {
            if (!visited[v]) {
                bfs(v);                   // 对未访问过的顶点进行BFS
            }
        }
    }

    private void bfs(int s) {
        Queue<Integer> queue = new LinkedList<>();   // 创建队列用于存储待访问顶点
        queue.add(s);                                // 将起始顶点入队
        visited[s] = true;                           // 标记起始顶点为已访问

        while (!queue.isEmpty()) {                   // 当队列不为空时,继续遍历
            int v = queue.remove();                     // 出队一个顶点v
            order.add(v);                             // 将v添加到遍历顺序中

            for (int w : G.adj(v)) {                   // 遍历v的邻接顶点w
                if (!visited[w]) {                     // 如果w未被访问过
                    visited[w] = true;                 // 标记w为已访问
                    order.add(w);                      // 将w入队
                }
            }
        }
    }

    public Iterable<Integer> order() {
        return order;                                // 返回顶点的遍历顺序
    }

    public static void main(String[] args) {
        Graph graph = new Graph("cc.txt");
        GraphBFS graphBFS = new GraphBFS(graph);
        System.out.println(graphBFS.order());
    }
}

在上述代码中,使用了一个队列来辅助实现广度优先遍历。首先将起始节点入队列,然后循环执行以下操作:出队列一个节点,对该节点进行处理,并将其标记为已访问,然后将该节点的未访问过的邻接节点按顺序入队列。通过不断出队列和入队列的操作,可以按照广度优先的顺序遍历图中的节点。

广度优先遍历的特点是从起始节点开始逐层向外扩展,先访问离起始节点最近的节点,再访问稍远的节点,直到遍历到图中所有可达节点。这种遍历方式可以用于求解最短路径、连通性问题等。

值得注意的是,在处理图的广度优先遍历时,需要使用额外的数据结构来记录已访问的节点,以防止重复访问和陷入循环。常用的数据结构有集合(Set)或标记数组等。

2. 路径问题

路径问题

如果两个顶点在同一个联通分量中,那么它们之间一定存在路径。联通分量是指图中的一组顶点,这些顶点之间可以相互连通,通过边进行路径的传递。

单源路径问题是指在给定的图中,找到从单个源顶点到其他所有顶点的路径。下面是使用广度优先搜索(DFS)来解决单源路径问题的步骤:

package BFS;

/**
 * @author wushaopei
 * @create 2023-06-05 10:35
 */
import java.util.*;

/**
 * 图的广度优先遍历(BFS)
 * @author wushaopei
 * @create 2023-06-05 10:04
 */
/**
 * 图的单源路径问题,使用广度优先遍历(BFS)求解
 */
public class SingleSourcePath {

    private Graph G;                 // 图对象
    private boolean[] visited;       // 记录顶点是否访问过
    private int s;                   // 源顶点
    private int[] pre;               // 记录顶点在路径中的前一个顶点

    public SingleSourcePath(Graph G, int s) {
        this.G = G;
        this.s = s;

        visited = new boolean[G.V()];           // 初始化visited数组,默认所有顶点未访问
        pre = new int[G.V()];                   // 初始化pre数组,默认所有顶点前一个顶点为-1

        for (int v = 0; v < G.V(); v++) {
            pre[v] = -1;
        }

        bfs(s);                                // 从源顶点s开始进行BFS遍历
    }

    private void bfs(int s) {
        Queue<Integer> queue = new LinkedList<>();    // 创建队列用于存储待访问顶点
        queue.add(s);                                 // 将源顶点s入队
        visited[s] = true;                            // 标记源顶点s为已访问

        while (!queue.isEmpty()) {                     // 当队列不为空时,继续遍历
            int v = queue.poll();                       // 出队一个顶点v

            for (int w : G.adj(v)) {                     // 遍历顶点v的邻接顶点w
                if (!visited[w]) {                       // 如果w未被访问过
                    queue.add(w);                        // 将w入队
                    visited[w] = true;                   // 标记w为已访问
                    pre[w] = v;                          // 设置w在路径中的前一个顶点为v
                }
            }
        }
    }

    public boolean isConnectedTo(int t) {
        G.validateVertex(t);                      // 验证目标顶点t是否合法
        return visited[t];                        // 返回目标顶点t是否与源顶点s相连
    }

    public Iterable<Integer> path(int t) {
        List<Integer> res = new ArrayList<>();
        if (!isConnectedTo(t)) return res;         // 若目标顶点t与源顶点s不相连,则返回空路径

        int cur = t;
        while (cur != s) {
            res.add(cur);                          // 将当前顶点加入路径中
            cur = pre[cur];                        // 更新当前顶点为其前一个顶点
        }
        res.add(s);                                // 将源顶点s加入路径中

        Collections.reverse(res);                   // 反转路径列表,得到从源顶点s到目标顶点t的路径
        return res;                                 // 返回路径列表
    }

    public static void main(String[] args) {
        Graph graph = new Graph("cc.txt");
        SingleSourcePath singleSourcePath = new SingleSourcePath(graph, 0);
        System.out.println(singleSourcePath.path(6));  // 打印从源顶点0到顶点6的路径
    }
}

通过广度优先遍历算法,从源顶点s开始逐层遍历图中的顶点,通过队列的先进先出特性,保证了路径的最短性。在遍历过程中,记录每个顶点的前驱顶点,最终可以通过回溯路径找到从源顶点到目标顶点的路径。

3. 深度优先遍历与广度优先遍历对比

深度优先遍历:

  • 从顶点0开始,递归地深度优先遍历子顶点。
  • 遍历路径为0->1->3->2->6。
  • 当到达顶点1时,发现顶点3已经被访问过,因此回溯到顶点1的父节点0。
  • 继续遍历顶点2,然后到达顶点6。
  • 最终,遍历了顶点0、1、3、2、6。

广度优先遍历:

  • 从顶点0开始,通过队列进行广度优先遍历。
  • 遍历路径为0->1->2->3->4->6。
  • 首先遍历顶点0,然后遍历子顶点1和2。
  • 在遍历顶点1时,遍历其子顶点3和4。
  • 在遍历顶点2时,由于顶点3已经被访问过,因此跳过它。
  • 最后,遍历顶点4和6。
  • 最终,遍历了顶点0、1、2、3、4、6。

路径比较:

  • 深度优先遍历的路径为0->1->3->2->6,包含了顶点0、1、3、2、6,共计5个顶点。
  • 广度优先遍历的路径为0->2->6,包含了顶点0、2、6,共计3个顶点。

综上所述,广度优先遍历具有以下优势:

  • 广度优先遍历是一种最短路径算法,可以找到从起始顶点到目标顶点的最短路径。
  • 在示例中,广度优先遍历找到了从顶点0到顶点6的最短路径,而深度优先遍历的路径长度更长。
  • 广度优先遍历通过逐层遍历子顶点,可以保证找到的路径是最短的。

4. 最短路径长度

package BFS;

/**
 * @author wushaopei
 * @create 2023-06-05 10:35
 */
import java.util.*;

/**
 * 图的广度优先遍历(BFS)
 * @author wushaopei
 * @create 2023-06-05 10:04
 */

/**
 * 图的单源路径问题,使用广度优先遍历(BFS)求解
 */
public class USSSPath {

    private Graph G;                 // 图对象
    private boolean[] visited;       // 记录顶点是否访问过

    private int s;                   // 源顶点
    private int[] pre;               // 记录顶点在路径中的前一个顶点
    private int[] dis;

    public USSSPath(Graph G, int s) {
        this.G = G;
        this.s = s;

        visited = new boolean[G.V()];           // 初始化visited数组,默认所有顶点未访问
        pre = new int[G.V()];                   // 初始化pre数组,默认所有顶点前一个顶点为-1
        dis = new int[G.V()];

        for (int v = 0; v < G.V(); v++) {
            pre[v] = -1;
        }

        bfs(s,dis);                                // 从源顶点s开始进行BFS遍历
    }

    private void bfs(int s, int[] dis) {
        Queue<Integer> queue = new LinkedList<>();    // 创建队列用于存储待访问顶点
        queue.add(s);                                 // 将源顶点s入队
        visited[s] = true;                            // 标记源顶点s为已访问
        dis[s] = 0;
        while (!queue.isEmpty()) {                     // 当队列不为空时,继续遍历
            int v = queue.poll();                       // 出队一个顶点v

            for (int w : G.adj(v)) {                     // 遍历顶点v的邻接顶点w
                if (!visited[w]) {                       // 如果w未被访问过
                    queue.add(w);                        // 将w入队
                    visited[w] = true;                   // 标记w为已访问
                    pre[w] = v;                          // 设置w在路径中的前一个顶点为v
                    dis[w] = dis[v] + 1;
                }
            }
        }
    }

    public boolean isConnectedTo(int t) {
        G.validateVertex(t);                      // 验证目标顶点t是否合法
        return visited[t];                        // 返回目标顶点t是否与源顶点s相连
    }

    public Iterable<Integer> path(int t) {
        List<Integer> res = new ArrayList<>();
        if (!isConnectedTo(t)) return res;         // 若目标顶点t与源顶点s不相连,则返回空路径

        int cur = t;
        while (cur != s) {
            res.add(cur);                          // 将当前顶点加入路径中
            cur = pre[cur];                        // 更新当前顶点为其前一个顶点
        }
        res.add(s);                                // 将源顶点s加入路径中

        Collections.reverse(res);                   // 反转路径列表,得到从源顶点s到目标顶点t的路径
        return res;                                 // 返回路径列表
    }

    public int dis(int s){
        G.validateVertex(s);
        return dis[s];
    }

    public static void main(String[] args) {
        Graph graph = new Graph("g.txt");
        USSSPath usssPath = new USSSPath(graph, 0);
        System.out.println(usssPath.path(6));  // 打印从源顶点0到顶点6的路径
        System.out.println(usssPath.dis(3)); // 打印源到顶点的路径长度
    }
}

该代码实现了使用广度优先遍历求解图的单源路径问题,并提供了打印路径和计算最短距离的功能。在主方法中,通过创建图对象和USSSPath对象,可以对图进行处理并输出结果。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高频面试八股文用法篇(二) hive中几种排序类型区别

目录 排序函数 1、order by 2、sort by 3、distribute by 4、cluster by 总结 排序类型 1、order by order by是与关系型数据库的用法是一样的。select * from emp order by empno desc; 针对全局数据进行排序&#xff0c;所以最终只会有1个reduce&#xff0c;因…

开源 Golang 微服务入门一: HTTP 框架 Hertz

前言 从本篇笔记开始将介绍 Go 框架三件套&#xff08;Web / RPC / ORM&#xff09;&#xff0c;框架的学习有助于后续课程的学习以及大项目的完成。本文主要介绍字节跳动的开源 Golang 微服务 HTTP 框架 Hertz。先了解一下三件套的相关基本知识&#xff0c;做一下铺垫&#x…

ArgoCD(二)--部署

3.2 ArgoCD部署 ArgoCD部署官网&#xff1a;https://argo-cd.readthedocs.io/en/stable/getting_started/ ArgoCD有两种部署方式&#xff1a;多租户部署和核心化部署&#xff1a; 多租户 常用于多个应用程序开发团队提供服务&#xff0c;并由平台团队维护的场景&#xff1b; …

BPMN模拟动画执行流程

目录 第一步&#xff1a;构建BPMN图 第二步&#xff1a;开启模拟 第三步&#xff1a;执行模拟 第四步&#xff1a;监听模拟 第一步&#xff1a;构建BPMN图 通过id标记&#xff0c;每一个流程 第二步&#xff1a;开启模拟 BPMN官方提供了各种各样的模块&#xff0c;比如执行…

修改linux ssh 22 端口

1、找到 sshd 的配置文件&#xff0c;增加一行 Port 32586 ,默认是 22 端口&#xff0c;记得&#xff0c;先不要把 22 端口的这一行删除&#xff0c;或者注释&#xff0c;因为我们要先验证一下&#xff0c;我们修改后的端口是否可以使用&#xff0c;都ok后&#xff0c;再把 22 …

精通Java数组的艺术:从初学者到高手的进阶之路(二)

⭐ 多维数组⭐ 数组存储表格数据⭐ Comparable 接口 ⭐ 多维数组 多维数组可以看成以数组为元素的数组。可以有二维、三维、甚至更多维数组&#xff0c;但是实际开发中用的非常少。最多到二维数组。 【eg】二维数组的声明 public class Test {public static void main(Strin…

一起看 I/O | Wear OS 更新一览

作者 / Android 开发者关系工程师 Kseniia Shumelchyk 随着 Wear OS 平台的不断发展&#xff0c;我们很高兴与您分享一些最新的功能和改进&#xff0c;以帮助您为用户打造富有吸引力的创新体验。 Peloton 和 Todoist 等合作伙伴一直以来都针对 Wear OS 打造卓越体验&#xff0c…

Android自定义一个车牌字母选择键盘

在一般和车相关的应用&#xff0c;难免会和车牌打交道&#xff0c;组成车牌的要素&#xff0c;国内无非就是省份简称地区代码英文或者数字组成&#xff0c;比如京A12345&#xff0c;在需要输入车牌的功能上&#xff0c;就需要有省份简称键盘和英文数字键盘了&#xff0c;在上篇…

深度学习(自编码器)

深度学习目录 自适应线性单元 (Widrow and Hoff, 1960)神经认知机 (Fukushima, 1980)GPU-加速 卷积网络 (Chellapilla et al., 2006)深度玻尔兹曼机 (Salakhutdinov and Hinton, 2009a)无监督卷积网络 (Jarrett et al., 2009b)GPU-加速 多层感知机 (Ciresan et al., 2010)分布…

人工影响天气期末复习笔记

&#xff08;一&#xff09;什么是人工影响天气 利用自然云微物理不稳定性&#xff0c;通过一定的技术方法改变云的微结构&#xff0c;从而改变云降水的发展过程&#xff0c;从而达到增加降水&#xff0c;防雹&#xff0c;消云雾等目的 &#xff08;二&#xff09;为什么要人工…

【历史上的今天】6 月 6 日:世界 IPv6 启动纪念日;《俄罗斯方块》发布;小红书诞生

整理 | 王启隆 透过「历史上的今天」&#xff0c;从过去看未来&#xff0c;从现在亦可以改变未来。 今天是 2023 年 6 月 6 日&#xff0c;在 2019 年的今天&#xff0c;工信部正式发放 5G 牌照。这一天&#xff0c;有四家企业被颁发了基础电信业务经营许可证&#xff0c;从此…

社区团购系统源码后台解析

近年来&#xff0c;随着购物方式的改变&#xff0c;社区团购可以说是越来越受关注了&#xff0c;大家应该对社区团购多多少少有一些认知&#xff0c;其实社区团购这样的商业模式拥有强大的赚钱的潜力&#xff0c;主要就是因为它的运营成本低&#xff0c;而且上手也不需要很复杂…

FPGA设计的指导性原则 (四)

在FPGA Express/FPGA Compiler II中,用鼠标右键单击编译后的芯片图标, 在弹出的命令对话框中选择“Edit Constraints”命令编辑综合约束文件(扩展 名为CTL),选择端口(Ports)选项卡,指定所需信号的全局时钟域为 “DONT USE”。图22所示为在FPGA Express综合约束编辑器中…

私有化部署低代码开发工具:jvs-rules 规则引擎决策流参数说明

JVS规则引擎决策调用 通过决策流水号查询入参变量 [请求参数]决策流 ​ GET/mgr/risk//test/parameter/flow/{no} 请求数据类型 application/x-www-form-urlencoded 响应数据类型 [ "*/*" ] 请求参数 参数名称 参数说明 请求类型 是否必须 数据类型 sch…

【Flutter混合开发】开发一个简单的快速启动框架

目录 前言启动插件Flutter代码Android代码IOS代码 启动模块使用android端ios端 前言 因为在移动端中启动Flutter页面会有短暂空白&#xff0c;虽然官方提供了引擎预热机制&#xff0c;但是需要提前将所有页面都进行预热&#xff0c;这样开发成本较高&#xff0c;在研究了闲鱼的…

通过点引导掩码表示的弱半监督实例分割

文章目录 The Devil is in the Points: Weakly Semi-Supervised Instance Segmentation via Point-Guided Mask Representation摘要本文方法Weakly Semi-Supervised Instance Segmentation using Point LabelsMask Refinement Network 实验结果消融实验 The Devil is in the Po…

【JavaEE】HTTP状态码-HTTP数据报的构造

HTTP状态码HTTP数据报的构造 文章目录 JavaEE & HTTP状态码 & HTTP数据报的构造1. HTTP状态码1.1 200 - OK1.2 404 - Not Found1.3 403 - Forbidden1.4 500 - Internal Server Error1.5 504 - Gateway Timeout1.6 302/301 重定向 2. 构造HTTP请求2.1 浏览器搜索栏输入u…

Express应用之记账本项目总结

前言 在学完nodejs相关知识后第一个实践就是这个记账本项目&#xff0c;本篇文章是对项目遇到的问题的总结。 先聊聊技术栈&#xff1a; 前端技术&#xff1a;h5结合bootstrap框架&#xff1b; 后端技术&#xff1a;nodejsExpress框架lowdb数据库。 gitee地址&#xff1a;ht…

FinalShell界面左侧为什么能够监测系统指标动态变化的原理并用python实现

前言&#xff1a; 我们可以看出FinalShell是用Java写的&#xff0c;具体怎么看出来的&#xff0c;不能光看界面logo是Java的logo&#xff0c;还要进它的安装目录下进行查看是否真是用Java编写的&#xff01;&#xff01;&#xff01; 具体查看如下&#xff1a; 查看finalshe…

软件外包开发在线监测工具

软件系统上线后需要在线网络工具监测系统的运行&#xff0c;这样在系统出现故障时第一时间通知到系统维护人员&#xff0c;对于软件系统的稳定运行是必不可少的监测工具。今天和大家分享一些常用的在线监测工具&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#…