深度优先搜索和广度优先搜索的java代码实现过程详解

news2024/11/20 11:21:06

深度优先搜索和广度优先搜索

在很多情况下,我们需要遍历图,得到图的一些性质,例如,找出图中与指定的顶点相连的所有顶点,或者判定某个顶点与指定顶点是否相通,是非常常见的需求。
有关图的搜索,最经典的算法有深度优先搜索广度优先搜索,接下来我们分别讲解这两种搜索算法。

1. 深度优先搜索

1.1 定义

所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找子结点,然后找兄弟结点。
在这里插入图片描述

1.2 API设计

类名DepthFirstSearch
构造方法DepthFirstSearch(Graph G,int s):构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点的所有相通顶点
成员方法1.private void dfs(Graph G, int v):使用深度优先搜索找出G图中v顶点的所有相通顶点
2.public boolean marked(int w):判断w顶点与s顶点是否相通
3.public int count():获取与顶点s相通的所有顶点的总数
成员变量1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索
2.private int count:记录有多少个顶点与s顶点相通

1.3 代码实现

注意:在类中导入自己写的Queue,代码如下

import java.util.Iterator;

public class Queue<T> implements Iterable<T>{
    //记录首结点
    private Node head;
    //记录最后一个结点
    private Node last;
    //记录队列中元素的个数
    private int N;


    private class Node{
        public T item;
        public Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
    public Queue() {
        this.head = new Node(null,null);
        this.last=null;
        this.N=0;
    }

    //判断队列是否为空
    public boolean isEmpty(){
        return N==0;
    }

    //返回队列中元素的个数
    public int size(){
        return N;
    }

    //向队列中插入元素t
    public void enqueue(T t){

        if (last==null){
            //当前尾结点last为null
            last= new Node(t,null);
            head.next=last;
        }else {
            //当前尾结点last不为null
            Node oldLast = last;
            last = new Node(t, null);
            oldLast.next=last;
        }

        //元素个数+1
        N++;
    }

    //从队列中拿出一个元素
    public T dequeue(){
        if (isEmpty()){
            return null;
        }

        Node oldFirst= head.next;
        head.next=oldFirst.next;
        N--;

        //因为出队列其实是在删除元素,因此如果队列中的元素被删除完了,需要重置last=null;

        if (isEmpty()){
            last=null;
        }
        return oldFirst.item;
    }


    @Override
    public Iterator<T> iterator() {
        return new QIterator();
    }

    private class QIterator implements Iterator{
        private Node n;

        public QIterator(){
            this.n=head;
        }
        @Override
        public boolean hasNext() {
            return n.next!=null;
        }

        @Override
        public Object next() {
            n = n.next;
            return n.item;
        }
    }


}
public class DepthFirstSearch {
    public static void main(String[] args) {
        //准备Graph对象
        Graph G = new Graph(13);
        G.addEdge(0,5);
        G.addEdge(0,1);
        G.addEdge(0,2);
        G.addEdge(0,6);
        G.addEdge(5,3);
        G.addEdge(5,4);
        G.addEdge(3,4);
        G.addEdge(4,6);
        G.addEdge(7,8);
        G.addEdge(9,11);
        G.addEdge(9,10);
        G.addEdge(9,12);
        G.addEdge(11,12);

        DepthFirstSearch search = new DepthFirstSearch(G,0);

        int count = search.count();
        System.out.println("与起点0相同的顶点的数量为:" + count);

        boolean marked1  = search.marked(5);
        System.out.println("顶点5和0是否相通:" + marked1);
        boolean marked2  = search.marked(7);
        System.out.println("顶点7和0是否相通:" + marked2);

    }
    private boolean[] marked; //索引代表顶点,值表示当前顶点是否已经被搜索
    private int count;//记录有多少个顶点与s顶点相通
    public DepthFirstSearch(Graph G, int s){//构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点的所有相邻顶点
        marked = new boolean[G.V()];//创建一个和图的顶点数一样大小的布尔数组
        dfs(G,s);//搜索G图中与顶点s相同的所有顶点

    }
    private void dfs(Graph G, int v){//使用深度优先搜索找出G图中v顶点的所有相邻顶点
        marked[v]=true;//把当前顶点标记为已搜索
        for(Integer w: G.adj(v)){//遍历v顶点的邻接表,得到每一个顶点w
            if(!marked[w]){//如果当前顶点w没有被搜索过,则递归搜索与w顶点相通的其他顶点
                dfs(G,w);
            }
        }
        count++;//相通的顶点数量+1

    }
    public boolean marked(int w)//判断w顶点与s顶点是否相通
    {
        return  marked[w];
    }
    public int count(){ //获取与顶点s相通的所有顶点的总数
        return count;
    }

    public static class Graph {
        private final int V;// 顶点数目
        private int E;// 边的数目
        private Queue<Integer>[] adj;// 邻接表

        public Graph(int V) {
            this.V = V;//初始化顶点数量
            this.E = 0;//初始化边的数量
            this.adj = (Queue<Integer>[]) new Queue[V];// 创建邻接表
            for (int i = 0; i < adj.length; i++) {//初始化邻接表中的空队列
                adj[i]= new Queue<Integer>() ;
            }

        }

        public int V() {//获取顶点数目
            return V;

        }

        public int E() {//获取边的数目
            return E;
        }

        public void addEdge(int v, int w) {
            //把w添加到v的链表中,这样顶点v就多了一个相邻点w
            adj[v].enqueue(w);
            //把v添加到w的链表中,这样顶点w就多了一个相邻点v
            adj[w].enqueue(v);
            //边的数目自增1
            E++;
        }

        public Queue<Integer> adj(int v) { //获取和顶点v相邻的所有顶点
            return  adj[v];
        }
    }





1.4 运行结果

与起点0相同的顶点的数量为:7
顶点50是否相通:true
顶点70是否相通:false

2. 广度优先搜索

2.1 定义

所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找兄弟结点,然后找子结点。在这里插入图片描述

2.2 API设计

类名BreadthFirstSearch
构造方法BreadthFirstSearch(Graph G,int s):构造广度优先搜索对象,使用广度优先搜索找出G图中s顶点的所有相邻顶点
成员方法1.private void bfs(Graph G, int v):使用广度优先搜索找出G图中v顶点的所有相邻顶点
2.public boolean marked(int w):判断w顶点与s顶点是否相通
3.public int count():获取与顶点s相通的所有顶点的总数
成员变量1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索
2.private int count:记录有多少个顶点与s顶点相通
3.private Queue waitSearch: 用来存储待搜索邻接表的点

2.3 代码实现

package heimadatapractise;

public class BreadthFirstSearch {
    public static void main(String[] args) {
        //准备Graph对象
        Graph G = new Graph(13);
        G.addEdge(0,5);
        G.addEdge(0,1);
        G.addEdge(0,2);
        G.addEdge(0,6);
        G.addEdge(5,3);
        G.addEdge(5,4);
        G.addEdge(3,4);
        G.addEdge(4,6);
        G.addEdge(7,8);
        G.addEdge(9,11);
        G.addEdge(9,10);
        G.addEdge(9,12);
        G.addEdge(11,12);

        BreadthFirstSearch search = new BreadthFirstSearch(G,0);

        int count = search.count();
        System.out.println("与起点0相同的顶点的数量为:" + count);

        boolean marked1  = search.marked(5);
        System.out.println("顶点5和0是否相通:" + marked1);
        boolean marked2  = search.marked(7);
        System.out.println("顶点7和0是否相通:" + marked2);

    }
    private boolean[] marked;
    private int count;
    private Queue<Integer> waitSearch;
    public BreadthFirstSearch(Graph G, int s){
        marked = new boolean[G.V()];
        waitSearch = new Queue<Integer>();
        dfs(G,s);
    }
    private void dfs(Graph G, int v){//使用广度优先搜索找出G图中v顶点的所有相邻顶点
        marked [v] = true;  //把当前顶点v标识为已搜索
        waitSearch.enqueue(v);         //让顶点v进入队列,待搜索
        while(!waitSearch.isEmpty()){ //通过循环,如果队列不为空,则从队列中弹出一个待搜索的顶点进行搜索
            Integer wait = waitSearch.dequeue(); //弹出一个待搜索的顶点
            for(Integer w: G.adj(wait)){  //遍历wait顶点的邻接表
                if(!marked[w]){ // 该顶点还没被搜索过 对其进行搜索
                    marked[w] = true;  // 将节点放入堆栈中,用于后续的获取该节点的子节点
                    waitSearch.enqueue(w);
                    count++;   //让相通的顶点+1;
                }
            }
        }

    }
    public boolean marked(int w)//判断w顶点与s顶点是否相通
    {
        return  marked[w];
    }
    public int count(){ //获取与顶点s相通的所有顶点的总数
        return count;
    }

    public static class Graph {
        private final int V;// 顶点数目
        private int E;// 边的数目
        private Queue<Integer>[] adj;// 邻接表

        public Graph(int V) {
            this.V = V; //初始化顶点数量
            this.E = 0;//初始化边的数量
            this.adj = (Queue<Integer>[]) new Queue[V];// 创建邻接表
            for (int i = 0; i < adj.length; i++) {//初始化邻接表中的空队列
                adj[i]= new Queue<Integer>() ;
            }

        }

        public int V() {//获取顶点数目
            return V;

        }

        public int E() {//获取边的数目
            return E;
        }

        public void addEdge(int v, int w) {
            //把w添加到v的链表中,这样顶点v就多了一个相邻点w
            adj[v].enqueue(w);
            //把v添加到w的链表中,这样顶点w就多了一个相邻点v
            adj[w].enqueue(v);
            //边的数目自增1
            E++;
        }

        public Queue<Integer> adj(int v) { //获取和顶点v相邻的所有顶点
            return  adj[v];
        }
    }

}

2.4 运行结果

与起点0相同的顶点的数量为:6
顶点50是否相通:true
顶点70是否相通:false

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

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

相关文章

Spring Security怎么自定义登录页? 怎么注销?

本章内容 怎么自定义登录页底层都怎么实现如何注销?注销底层源码简单分析 开干 去网上找个好看的前端, 改改改改spring security配置启动几个红框框的地方注意下 Configuration public class SecurityConfig {Beanpublic SecurityFilterChain securityFilterChain(HttpSecu…

【决策树】简单介绍+个人理解(二)

1、ID3(Iterative Dichotomizer) ID3是Quinlan于1986年提出的, 它的提出开创了决策树算 法的先河, 而且是国际上最早的决策树方法, 在该算法中, 引入了信息论中熵的概念, 利用分割前后的熵来计算信息 增益, 作为判别能力的度量。ID3 算法的核心是在决策树各个结点上应用信息增…

T-SQL程序练习04

目录 一、写一个存储过程 &#x1d439;&#x1d456;&#x1d44f;&#x1d45c;&#x1d45b;&#x1d44e;&#x1d450;&#x1d450; 1. 具体要求 2. T-SQL程序代码 3. 结果显示 二、建立存储过程 &#x1d446;&#x1d44e;&#x1d45b;&#x1d43a;&#x1d462;…

万向区块链肖风:元宇宙的十大经济规则

本文为万向区块链董事长兼总经理肖风为华泰证券研究所科技及电子行业首席分析师黄乐平、万向区块链首席经济学家邹传伟联合撰写的《元宇宙经济学》所作序言。 元宇宙是什么&#xff1f;按照我的理解&#xff0c;元宇宙是一个由分布式网络技术、分布式账本和分布式社会/商业构成…

消息中间件介绍

一般&#xff0c;我们认为消息中间件是指支持与保障分布式应用程序之间同步/异步收发消息的中间件。消息是分布式应用之间进行数据交换的基本信息单位&#xff0c;分布式应用程序之间的通信接口由消息中间件提供。其中&#xff0c;异步方式指消息发送方在发送消息时不必知道接收…

奇遇MIX体验:加入全彩VST透视,开创消费级VR一体机新时代

前不久在奇遇MIX发布会上我们知道&#xff0c;面临着国内复杂的竞争环境&#xff0c;奇遇VR将选择差异化运营模式&#xff0c;一是硬件上停产单一VR模式设备&#xff0c;专注于支持VST的VR设备&#xff1b;二是内容层面&#xff0c;通过提供三年影视或游戏权益的模式&#xff0…

【openGauss实战1】openGauss基于CentOS8的部署

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61…

写出更优雅和稳健的 TS 代码的几个 tips

写出更优雅和稳健的 TS 代码的几个 tips 本来想放优雅 太优雅了.jpg&#xff0c;后来还是好懒啊…… 使用 unknown 代替 any any 的问题在于它直接关闭了 TS 的类型检查&#xff0c;因此一旦使用了 any&#xff0c;那就代表任何事情都会发生。使用 unknown 则告诉 TS&#x…

LIS源码 医院检验科LIS系统源码 .net检验系统源码 实验室信息管理系统源码全开源,价值百万

LIS系统即实验室信息管理系统。LIS系统能实现临床检验信息化&#xff0c;检验科信息管理自动化。其主要功能是将检验科的实验仪器传出的检验数据经数据分析后&#xff0c;自动生成打印报告&#xff0c;通过网络存储在数据库中&#xff0c;使医生能够通过医生工作站方便、及时地…

PXE+Kickstart 自动化部署系统

PXE 预启动执行环境是由Intel开发的技术,可以让计算机通过网络来启动操作系统(前提是计算机上安装的网卡支持PXE技术),主要用于在无人值守安装系统中引导客户端主机安装Linux操作系统. Kickstart是一种无人值守的安装方式,其工作原理是预先把原本需要运维人员手工填写的参数保…

谷粒商城之高级篇(2)

2.6 购物车服务 2.6.1 环境搭建 ①域名配置 ②创建 微服务 暂时需要的插件 此外&#xff0c;导入 公共包的依赖 <dependency><groupId>com.atguigu.gulimall</groupId><artifactId>gulimall-common</artifactId><version>0.0.1-SNAPSHOT…

50. 残差网络(ResNet)代码实现

1. 残差块 ResNet沿用了VGG完整的 33 卷积层设计。 残差块里首先有2个有相同输出通道数的 33 卷积层。 每个卷积层后接一个批量规范化层和ReLU激活函数。 然后我们通过跨层数据通路&#xff0c;跳过这2个卷积运算&#xff0c;将输入直接加在最后的ReLU激活函数前。 这样的设计…

PDF在线转Word?方便快捷易上手的方法

PDF和Word都是我们日常生活中经常看到的文件格式&#xff0c;如果你也是一个工作党&#xff0c;那小编相信你肯定会遇到关于这两种文件的转换问题。其实&#xff0c;PDF格式是十分方便我们进行文件传输和差阅读&#xff0c;不仅兼容性较强&#xff0c;而且文件内容相对来说是固…

罗克韦尔AB PLC安装Studio 5000 V35的具体步骤演示

罗克韦尔AB PLC安装Studio 5000 V35的具体步骤演示 具体安装步骤可参考如下内容: 解压下载的安装包,找到安装包中的Setup.exe, 如下图所示,右击Setup.exe,选择“以管理员身份运行”, 如下图所示,安装程序正在准备中, 如下图所示,此时安装程序报错:未安装Microsoft…

ARM64内存虚拟化分析(6)向KVM注册内存更新

1 KVM memory listener的注册 在KVM初始化kvm_init()中会通过函数km_memory_listener_regiter()注册KVM所对应的memory listener&#xff0c;其中设置KVM region_add回调&#xff0c;KVM region_del回调以及KVM log_start/log_stop的回调。 2 region_add回调 当添加内存区域时&…

Wireshark抓到的H264帧

H264文件解析 NALU size, NALU start code size, NALU type 0, 0 0 UNSPECIFIED NALU size, NALU start code size, NALU type 26, 4 7 SPS NALU size, NALU start code size, NALU type 4, 4 8 PPS NALU size, NALU start code…

浅谈数据孤岛和数据分析的发展

大数据时代&#xff0c;企业对数据的重视力度逐步增强&#xff0c;数据分析、数据治理、数据管理、数据资产&#xff0c;已经被人们熟知&#xff0c;在数据的统计汇总和挖掘分析下&#xff0c;管理者的决策有了强有力的支撑和依据&#xff0c;同时也产生了新的问题&#xff0c;…

CSS 奇技淫巧Box-shadow实现圆环进度条

CSS 奇技淫巧Box-shadow实现圆环进度条 文章目录CSS 奇技淫巧Box-shadow实现圆环进度条一、Box-shadow圆环进度条二、效果预览三、原理刨析四、实际应用五、总结六、参考资料&#x1f498;七、推荐博文&#x1f357;一、Box-shadow圆环进度条 实现圆环进度条的方法用很多种&am…

figma和sketch应该选择哪个?

设计行业的工具层出不穷&#xff0c;在我看来sketch它在一定程度上被颠覆了PS&#xff0c;如今sketch已经成为许多设计团队的设计工具。 那么Figma相对于Sketch自身优势是什么&#xff1f;有什么不便&#xff1f;让我们从几个方面来了解。 两个软件都很适合创建UI和组件库。Sk…

图形查看器丨IrfanView功能简介

IrfanView 是一款快速、紧凑和创新的图形查看器&#xff0c;适用于Windows XP、Vista、7、8、10和11。 IrfanView寻求创建独特、新颖和有趣的功能&#xff0c;与其他一些图形查看器不同&#xff0c;它们的全部“创造力”是基于功能克隆、窃取想法和来自ACDSee和/或IrfanView的整…