【图】深度优先遍历 广度优先遍历

news2024/12/31 6:04:11

文章目录

    • 一、广度优先遍历
    • 二、深度优先遍历

深度优先遍历广度优先遍历是遍历图的两种常见方式,接下来就通过这两种方式来实现一下图具体遍历的过程

当我位于游乐园的景区 A 时,为了玩遍所有的景区我们有两种玩的方式:

方式一:

  • 先依次将景区 A 附近相邻的所有景区(B、C、D)都玩个遍
  • 然后依次将景区 B、C、D 附近相邻的所有景区玩个遍
  • 以此类推,直到景区都玩遍为止

方式二:

  • 先选择一个景区 A 附近相邻的景区(B)进行游玩
  • 然后再选择一个景区 B 附近相邻的景区(E)进行游玩
  • 一头扎进游乐园,不断的深入,如果发现景区 X 周围的景点都玩过了,就进行回退,回退时如果发现有没有玩过的景区支路,就可以进入该支路继续深入,直到景区都玩遍为止

方式一实际上就是广度优先遍历(B F S)的过程,一层一层的由内而外扩张,遍历的方式有点像二叉树的层序遍历(自上而下)

方式二实际上就是深度优先遍历(D F S)的过程,深度搜索,走到尽头再不断回退找其他可行的支路,遍历的方式有点像二叉树的前中后序遍历

接下来就将具体的通过下面的这张图来形象的展示两种方式遍历图的过程

在这里插入图片描述

一、广度优先遍历

广度优先遍历的实现是通过队列完成的

1)我们将从顶点 0 出发,将顶点 0 入队列

在这里插入图片描述

2)弹出顶点 0 ,将其附近的顶点1、2、3、4依次遍历放入队列中

在这里插入图片描述
在这里插入图片描述

3)接着,弹出顶点 1 ,将与其相邻的顶点 8 放入队列中。以此类推,我们将遍历与顶点 2、3、4相邻的未遍历过的顶点,将其添加到队列中

在这里插入图片描述
在这里插入图片描述

4)最后,将遍历与顶点 8、10、5、6相邻的未遍历过的顶点,依次出队入队

在这里插入图片描述
在这里插入图片描述

思路:
1、将所有已经遍历过的点都放到一个 set 中
2、将 node 放到 queue 中
3、从队列中弹出一个元素(打印)并将该元素相邻的所有未遍历过的节点都放到 queue 中
4、往复循环,直到 queue 为空,遍历完成

代码实现:

在上一节有介绍过图的标准模板,如果只是完成遍历操作,可以仅保留以下信息

//顶点
public class Node {
    //点的值
    public Integer value;
    //有这个点发散出去的相邻的点有哪些
    public ArrayList<Node> nextNodes;

    public Node(Integer value) {
        this.value = value;
        nextNodes = new ArrayList<>();
    }
}
//图
public class Graph {
    //图中所有的点,key是点的值,value是对应的点的具体信息
    public HashMap<Integer,Node> nodes;

    public Graph() {
        nodes = new HashMap<>();
    }
}
//构造图
public static void main(String[] args) {
    Graph graph = new Graph();
    Node node = new Node(0);
    Node node1 = new Node(1);
    Node node2 = new Node(2);
    Node node3 = new Node(3);
    Node node4 = new Node(4);
    Node node5 = new Node(5);
    Node node6 = new Node(6);
    Node node7 = new Node(7);
    Node node8 = new Node(8);
    Node node9 = new Node(9);
    Node node10 = new Node(10);
    
    node.nextNodes.add(node1);
    node.nextNodes.add(node2);
    node.nextNodes.add(node3);
    node.nextNodes.add(node4);

    node1.nextNodes.add(node);
    node1.nextNodes.add(node2);
    node1.nextNodes.add(node8);

    node2.nextNodes.add(node);
    node2.nextNodes.add(node1);
    node2.nextNodes.add(node10);

    node3.nextNodes.add(node);

    node4.nextNodes.add(node);
    node4.nextNodes.add(node5);
    node4.nextNodes.add(node6);

    node5.nextNodes.add(node4);

    node6.nextNodes.add(node4);
    node6.nextNodes.add(node7);

    node7.nextNodes.add(node6);

    node8.nextNodes.add(node1);
    node8.nextNodes.add(node9);

    node9.nextNodes.add(node10);

    node10.nextNodes.add(node2);
    BFS(node);
}
//广度优先遍历
public void BFS(Node node) {
    if (node == null) {
        return;
    }
    Queue<Node> queue = new LinkedList<>();//一个队列
    HashSet<Node> set = new HashSet<>();//已经遍历过的顶点
    queue.offer(node);
    set.add(node);//添加到已遍历set中
    while (!queue.isEmpty()) {
        Node node1 = queue.poll();//弹出一个元素
        System.out.println(node1.value + " ");//打印元素
        for (Node n: node1.nextNodes) {
            if (!set.contains(n)) {
                //从前未遍历过的
                queue.offer(n);
                set.add(n);//添加到已遍历set中
            }
        }
    }
}

二、深度优先遍历

深度优先遍历的实现是通过栈完成的

1)依次访问顶点 0、1、2、10,依次入栈,此时栈顶是10,表示该支路已经走到了尽头

在这里插入图片描述

2)从顶点 10 回溯到顶点 1,依次出栈顶点 10、2

在这里插入图片描述

3)在顶点 1 处终于找到了新的支路,依次访问顶点 8、9,依次入栈

在这里插入图片描述

4)以此类推,接下来就会出栈顶点 9、8、1,回溯到顶点 0,走顶点 3这条支路,直到遍历完所有的顶点

通过上面的图我们可以明白回溯的过程,那么不断深入的过程又是怎样的呢?如何让栈始终保持的是深度路径?什么情况下出栈?

思路:
1、使用一个栈永远保持着深度的路径 
2、将所有已经遍历过的点都放到一个 set 中
3、将 node 放到栈中,添加到 set 中,进行打印,表示已遍历
4、只要栈不为空,就弹出一个元素
5、弹出的元素,将该元素相邻的没有遍历过的点找到一个后,就不继续找了,break 跳出
6、将弹出的元素以及找的点先后压入栈中,并在 set 中注册找到的点,进行打印,表示已遍历
7、直到所有的点都遍历过了,那么在栈不为空时,栈就只会一直的弹出点,直到栈为空

代码实现:

public static void DFS(Node node) {
    if(node == null) {
        return;
    }
    Stack<Node> stack = new Stack<>();//保持着深度路径的栈
    HashSet<Node> set = new HashSet<>();//已遍历的点
    stack.push(node);
    set.add(node);
    System.out.println(node.value);//打印
    while (!stack.isEmpty()) {
        Node node1 = stack.pop();//弹出一个元素
        for (Node n: node1.nextNodes) {
            if (!set.contains(n)) {
                //找到一个没遍历过的临近的点
                stack.push(node1);//之前弹出的点再压回去
                stack.push(n);//新找到的点压入栈中
                set.add(n);//已遍历该点
                System.out.println(n.value + " ");//打印
                break;//找到一个就跳出,下一轮循环中,再从这个点开始深度遍历
            }
        }
    }
}

当然我们也可以使用递归的方式,毕竟递归的本质就是基于方法调用栈来实现的

public static void DFS2(Node node,HashSet<Node> set) {
    System.out.println(node.value);//打印
    set.add(node);//表示已遍历
    for (Node n:node.nextNodes) {
        if (!set.contains(n)) {
            //找到一个没有遍历过的支路
            DFS2(n,set);
        }
    }
}

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

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

相关文章

HDLbits——移位寄存器

移位寄存器 1 4 位移位寄存器 module top_module(input clk,input areset, // async active-high reset to zeroinput load,input ena,input [3:0] data,output reg [3:0] q); always (posedge clk or posedge areset) beginif (areset)q<4h0;else if(load)q<data;els…

linux|shell脚本|有趣的知识---格式化输出日志和脚本调试方法以及kubernetes集群核心服务重启和集群证书备份脚本

前言&#xff1a; shell脚本的功能十分强大&#xff0c;这一点毋庸置疑的。那么&#xff0c;平常的工作中总是免不了和脚本打交道&#xff0c;也免不了要自己编写一些脚本。 每个人都希望自己编写的脚本强壮&#xff0c;简单&#xff0c;易用&#xff0c;功能多&#xff0c;并…

CDH中某一结点任务异常,节点服务重启失败报错:No space left on device

文章目录Error Message - 报错信息Analysis Process - 分析思路Solution - 解决方案Error Message - 报错信息 今天发现cdh集群的某一个节点任务爆红了&#xff0c;因为是测试的服务器&#xff0c;一般我都会尝试直接重启&#xff0c;但是该节点服务关闭后&#xff0c;竟然都无…

Spring Web

目录 概述 SpringMVC的组件 DispatcherServlet HandlerMapping HandlerAdapter SpringWeb的运行流程 Controller类的编写 RestController注解 RequestMapping注解 SpringWeb搭建 获取请求参数 解决POST请求中文乱码问题 Ajax返回JSON数据 跨域问题的解决 拦截器 …

LabVIEW与SQL Server 2919 Express通讯

LabVIEW与SQL Server 2919 Express通讯 ​LabVIEW与数据库通讯&#xff0c;可以使用数据库连接工具包。一般小型数据库用Access就可以了。但是对于长时间&#xff0c;需要存储空间较大的场合&#xff0c;Access一般不超过2G。这样就需要更换其他数据了。 SQL Server不同版本存…

MDC Service 基于 ESP32 推出树莓派 4 形态的 EsPiFF

当您的应用在树莓派 4 上运行不够稳定或耗电量过大时&#xff0c;您可以考虑使用 EsPiFF。这是一款由 MDC-Service 基于乐鑫 ESP32 构建的开发板。EsPiFF 配备有线和无线以太网、SD 卡插槽和 RP2040 协处理器。如果您尚未找到适合您项目的树莓派&#xff0c;不妨尝试一下这款低…

使用 Docker 来快速上手中文 Stable Diffusion 模型:太乙

本篇文章&#xff0c;我们聊聊如何使用 Docker 快速运行中文 Stable Diffusion 模型&#xff1a;太乙。 写在前面 上个月的时候&#xff0c;有朋友和我推荐了一个 “Stable Diffusion” 模型&#xff0c;来自深圳大湾区数字经济研究院(IDEA)的封神榜大模型中的 “太乙” 。 最…

web期末大作业 使用HTML+CSS制作蓝色版爱宠之家带留言板(5页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Metal每日分享,4x4颜色矩阵滤镜效果

本案例的目的是理解如何用Metal实现图像4x4颜色矩阵效果滤镜,通过4x4矩阵对RGBA像素处理; Demo HarbethDemo地址实操代码 // 绿色通道加倍 let filter = C7ColorMatrix4x4(matrix: Matrix4x4.Color.greenDouble)// 方案1: ImageView.image = try? BoxxIO(element: originIm…

【Python开发】Flask中的单点登录解决方案

Flask中的单点登录解决方案1.SSO 和 CAS 单点登录&#xff08;Single Sign On&#xff0c;SSO&#xff09;就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后&#xff0c;即可获得访问单点登录系统中其他关联系统和应用软件的权限&#xff0c;同时这种实现…

E. DS哈希查找--Trie树

目录 题目描述 思路分析 AC代码 题目描述 Trie树又称单词查找树&#xff0c;是一种树形结构&#xff0c;如下图所示。 它是一种哈希树的变种。典型应用是用于统计&#xff0c;排序和保存大量的字符串&#xff08;但不仅限于字符串&#xff09;&#xff0c;所以经常被搜索引擎…

【经验版】Linux相关教程(二)

一、参考资料 二、常用指令 1. 安装run软件包 # 可执行权限 chmod x 软件包名.run# 校验软件包安装文件的一致性和完整性 ./软件包名.run --check# 指定安装路径 ./软件包名.run --install如果用户未指定安装路径&#xff0c;则软件会安装到默认路径下&#xff0c;默认安装路…

kafka一致性保证

1、概念 水位标记&#xff1a; 水位或水印&#xff08;watermark&#xff09;一词&#xff0c;表示位置信息&#xff0c;即位移&#xff08;offset&#xff09;。Kafka源码中使用的名字是高水位&#xff0c;HW&#xff08;high watermark&#xff09;。 副本角色&#xff1a;…

华硕编程竞赛11月JAVA专场 E题太空漫步 题解

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…

cleanmymac4.12最新版下载安装教程

cleanmymac2023的“智能扫描”功能略不同于两外两款软件。除垃圾扫描以外&#xff0c;它还连带有搜索mac潜在威胁以及寻找提升系统性能方案的功能。在垃圾文件分类方面&#xff0c;它将垃圾首先分为系统垃圾、iTunes垃圾、照片垃圾3大类&#xff0c;每一类再做具体细分。但这样…

AUTOSAR OTA升级

一、OTA技术概念 随着高级辅助驾驶的发展和自动驾驶的引入&#xff0c;汽车变得越来越智能&#xff0c;这些智能汽车被软件控制&#xff0c;装有巨量的软件程序&#xff0c;当出现一个软件程序问题或者更新时&#xff0c;如果 按照传统的解决方式 &#xff0c;那都将是一项很繁…

美腾科技科创板上市:预计年营收4.7亿到5.7亿 市值44亿

雷递网 雷建平 12月9日天津美腾科技股份有限公司&#xff08;简称&#xff1a;“美腾科技”&#xff0c;股票代码为&#xff1a;“688420”&#xff09;今日在科创板上市。美腾科技此次发行2211万股&#xff0c;发行价为48.96元&#xff0c;募资总额为10.83亿元。美腾科技开盘价…

Leetcode 1691. 堆叠长方体的最大高度 [Java/C++] 排序+动态规划(附详细证明过程)

给你 n 个长方体 cuboids &#xff0c;其中第 i 个长方体的长宽高表示为 cuboids[i] [widthi, lengthi, heighti]&#xff08;下标从 0 开始&#xff09;。请你从 cuboids 选出一个 子集 &#xff0c;并将它们堆叠起来。如果 widthi < widthj 且 lengthi < lengthj 且 h…

计算机网络(自顶向下)—第八章习题

在下面的空格中填入“谁的什么密钥”&#xff1a; &#xff08;1&#xff09; A 向 B 发送一个一次性会话密钥&#xff0c;A 用B的公钥加密该会话密钥。 &#xff08;2&#xff09; Certifier.com 用Certifier.com的私钥 为 foo.com 签发公钥证书。 &#xff08;3&#xff…

红队隧道应用篇之Neo-reGeorg实现内网穿透(四)

简介 reGeorg是一个能够实现内网穿透的工具&#xff0c;基于socks5协议&#xff0c;且能支持众多脚本 由于此工具使用率过高&#xff0c;导致容易被杀毒软件拦截, 现有一个项目是由reGeorg修改而来, 而且做了加密和免杀处理, 这款工具的名字就叫Neo-reGeorg Neo-reGeorg下载…