图的遍历-----深度优先遍历(dfs),广度优先遍历(bfs)【java详解】

目录

简单介绍:什么是深度、广度优先遍历?

 深度优先搜索(DFS,Depth First Search):

大致图解:

 广度优先搜索(BFS,Breadth First Search):

大致图解:

一.图的创建(邻接矩阵)

 图的创建完整代码:

运行结果:

二.图的深度优先遍历(DFS):

遍历思想:

算法步骤:

 访问初始结点v:

 查找结点v的第一个邻接结点w:

深度搜索算法:

 ​编辑

 三.图的广度优先遍历(BFS):

广度优先算法:

深度 优先遍历 && 广度优先遍历的区别:

测试用例:

小结:


简单介绍:什么是深度、广度优先遍历?

图的遍历是指,从给定图中任意指定的顶点(称为初始点)出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每个顶点仅被访问一次,这个过程称为图的遍历。遍历过程中得到的顶点序列称为图遍历序列。

图的遍历过程中,根据搜索方法的不同,又可以划分为两种搜索策略:

  • 深度优先搜索(DFS,Depth First Search)
  • 广度优先搜索(BFS,Breadth First Search)

 深度优先搜索(DFS,Depth First Search):

遍历思想::首先从图中某个顶点v0出发,访问此顶点,标记已访问的顶点,然后依次从v0相邻的顶点出发深度优先遍历,直至图中所有与v路径相通的顶点都被访问了;若此时尚有顶点未被访问,则从中选一个顶点作为起始点,重复上述过程,直到所有的顶点都被访问。

大致图解:

 广度优先搜索(BFS,Breadth First Search):

遍历思想:首先,从图的某个顶点v0出发,访问了v0之后,依次访问与v0相邻的未被访问的顶点,然后分别从这些顶点出发,广度优先遍历,直至所有的顶点都被访问完。

大致图解:

 

一.图的创建(邻接矩阵)

 图的遍历基础是我们首先得有个图,这里我们创建一个图,用邻接矩阵的方法。这里我们创建一个如下图所示的图(左边是图,右边是图所对应的表示方法):

 图的创建完整代码:

import java.util.*;
public class Graph {private ArrayList<String> vertexList;//一个一维数组用于存储顶点的信息private int[][] edges;//一个二维数组用于存储对应边的信息private int numOfEdges;//记录边的个数private boolean[] isVisited;//判断顶点是否被访问//测试public static void main(String[] args){String vertexs[] = {"A","B","C","D","E"};Graph graph = new Graph(5);for(String vertex : vertexs){graph.insertVertex(vertex);}//插入的节点展示:System.out.println("插入的节点展示:");for(String vertex : vertexs){System.out.print(vertex+" ");}System.out.println();                 //  A B C D Egraph.insertEdge(0,1,1);              //A 0 1 1 0 0graph.insertEdge(0,2,1);              //B 1 0 1 1 1graph.insertEdge(1,2,1);              //C 1 1 0 0 0graph.insertEdge(1,3,1);              //D 0 1 0 0 0graph.insertEdge(1,4,1);              //E 0 1 0 0 0//创建的图展示:System.out.println("创建的图展示:");graph.showGraph();//边个数System.out.println("边个数:"+graph.numOfEdges);}//构造器public Graph(int n){vertexList = new ArrayList<String>(n);edges = new int[n][n];numOfEdges = 0;}//图的创建和展示--》方法//展示图public void showGraph(){for(int[] link : edges){System.out.println(Arrays.toString(link));}}//插入顶点public void insertVertex(String vertex){vertexList.add(vertex);}//插入边public void insertEdge(int v1,int v2,int weight){edges[v1][v2] = 1;edges[v2][v1] = 1;numOfEdges++;}
//图的常见方法//得到顶点个数public int getNumOfVertex(){return vertexList.size();}//通过索引得到对应的顶点public String gerValueByIndex(int i){return vertexList.get(i);}//得到对应边的权重public int getWeight(int v1,int v2){return edges[v1][v2];}
}

运行结果:

二.图的深度优先遍历(DFS):

遍历思想:

  1. 深度优先遍历,从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点, 可以这样理解:每次都在访问完当前结点后首先访问当前结点的第一个邻接结点。
  2. 我们可以看到,这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问。
  3. 显然,深度优先搜索是一个递归的过程

算法步骤:

  1. 访问初始结点v,并标记结点v为已访问。
  2. 查找结点v的第一个邻接结点w。
  3. 若w存在,则继续执行4,如果w不存在,则回到第1步,将从v的下一个结点继续。
  4. 若w未被访问,对w进行深度优先遍历递归(即把w当做另一个v,然后进行步骤123)。
  5. 查找结点v的w邻接结点的下一个邻接结点,转到步骤3。

 还是以上面创建的图为例:

 访问初始结点v:

//1.得到第一个邻接点的下标public int getFirstNeifhbor(int index){for(int j = 0;j < vertexList.size();j++){if(edges[index][j] > 0){return j;}}return -1;}

 查找结点v的第一个邻接结点w:

//2.根据前一个邻接点的下标获取下一个邻接点public int getNextNeighbor(int v1,int v2){for(int j = v2 + 1;j < vertexList.size();j++){if(edges[v1][j] > 0){return j;}}return -1;}

深度搜索算法:

//深搜遍历算法//first stepprivate void dfs(boolean[] isVisited,int i){System.out.print(getValueByIndex(i)+"->");isVisited[i] = true;int w = getFirstNeifhbor(i);while(w != -1){if(!isVisited[w]){dfs(isVisited,w);}w = getNextNeighbor(i,w);}}
//second steppublic void dfs(){isVisited = new boolean[vertexList.size()];for(int i = 0;i < vertexList.size();i++){if(!isVisited[i]){dfs(isVisited,i);}}}

遍历结果:

 

 三.图的广度优先遍历(BFS):

基本思想:

图的广度优先搜索(Broad First Search) 。 类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来访问这些结点的邻接结点

算法步骤:

  1. 访问初始结点v并标记结点v为已访问。
  2. 结点v入队列
  3. 当队列非空时,继续执行,否则算法结束。
  4. 出队列,取得队头结点u。
  5. 查找结点u的第一个邻接结点w。
  6. 若结点u的邻接结点w不存在,则转到步骤3;否则循环执行以下三个步骤:                    6.1若结点w尚未被访问,则访问结点w并标记为已访问。                                               6.2 结点w入队列                                                                                                              6.3查找结点u的继w邻接结点后的下一个邻接结点w,转到步骤6。

广度优先算法:

//对一个结点进行广度优先遍历的方法
private void bfs(boolean[] isVisited, int i) {int u ; // 表示队列的头结点对应下标int w ; // 邻接结点w//队列,记录结点访问的顺序LinkedList queue = new LinkedList();//访问结点,输出结点信息System.out.print(getValueByIndex(i) + "=>");//标记为已访问isVisited[i] = true;//将结点加入队列queue.addLast(i);while( !queue.isEmpty()) {//取出队列的头结点下标u = (Integer)queue.removeFirst();//得到第一个邻接结点的下标 w w = getFirstNeighbor(u);while(w != -1) {//找到//是否访问过if(!isVisited[w]) {System.out.print(getValueByIndex(w) + "=>");//标记已经访问isVisited[w] = true;//入队queue.addLast(w);}//以u为前驱点,找w后面的下一个邻结点w = getNextNeighbor(u, w); //体现出我们的广度优先}}} 
//遍历所有的结点,都进行广度优先搜索
public void bfs() {isVisited = new boolean[vertexList.size()];for(int i = 0; i < getNumOfVertex(); i++) {if(!isVisited[i]) {bfs(isVisited, i);}}
}

遍历结果: 

深度 优先遍历 && 广度优先遍历的区别:

 这里由于上面的图比较简单,看不出深度和广度优先遍历的区别,我在举一个图的栗子:

测试用例:

 public static void main(String[] args){int n = 8;  //结点的个数String Vertexs[] = {"1", "2", "3", "4", "5", "6", "7", "8"};//创建图对象Graph graph = new Graph(n);//循环的添加顶点for(String vertex: Vertexs) {graph.insertVertex(vertex);}//更新边的关系graph.insertEdge(0, 1, 1);graph.insertEdge(0, 2, 1);graph.insertEdge(1, 3, 1);graph.insertEdge(1, 4, 1);graph.insertEdge(3, 7, 1);graph.insertEdge(4, 7, 1);graph.insertEdge(2, 5, 1);graph.insertEdge(2, 6, 1);graph.insertEdge(5, 6, 1);//显示一把邻结矩阵System.out.println("图的创建:");graph.showGraph();//测试一把,我们的dfs遍历是否okSystem.out.println("深度优先遍历:");graph.dfs(); // A->B->C->D->E [1->2->4->8->5->3->6->7]System.out.println();System.out.println("广度优先遍历:");graph.bfs(); // A->B->C->D-E [1->2->3->4->5->6->7->8]}

运行结果:

小结:

图的深度优先遍历(Depth First Search,DFS)和广度优先遍历(Breadth First Search,BFS)是两种常用的图遍历算法,它们的主要区别如下:

  1. 遍历顺序:

    • DFS:从起始节点开始,沿着一条路径尽可能深入地遍历,直到无法继续深入为止,然后回溯到前一个节点,再选择另一条路径继续遍历,直到遍历完所有节点。
    • BFS:从起始节点开始,先遍历其所有相邻节点,然后再依次遍历这些相邻节点的相邻节点,以此类推,直到遍历完所有节点。
  2. 数据结构:

    • DFS:通常使用栈(Stack)数据结构来实现,通过递归或显式栈来保存遍历路径。
    • BFS:通常使用队列(Queue)数据结构来实现,通过将节点按照遍历顺序依次加入队列中。
  3. 遍历顺序的特点:

    • DFS:深度优先遍历更加注重深度,会优先探索离起始节点较远的节点,可能会在较深的层级上找到目标节点。
    • BFS:广度优先遍历更加注重广度,会优先探索离起始节点较近的节点,可能会在较浅的层级上找到目标节点。

结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固自己的知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+关注+收藏,你们的鼓励是我创作的最大动力!

 

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

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

相关文章

Python学习笔记——自定义函数(传递任意数量的实参)

Python允许函数从调用语句中收集任意数量的实参。例如下面自定义函数制作一个披萨&#xff0c;它需要接受很多配料&#xff0c;但无法预先确定顾客要点多少种配料。 下面行数只有一个形参*toppings&#xff0c;不管调用语句提供多少个实参&#xff0c;这个参数都会收集到&…

用 LangChain 和 Milvus 从零搭建 LLM 应用

如何从零搭建一个 LLM 应用&#xff1f;不妨试试 LangChain Milvus 的组合拳。 作为开发 LLM 应用的框架&#xff0c;LangChain 内部不仅包含诸多模块&#xff0c;而且支持外部集成&#xff1b;Milvus 同样可以支持诸多 LLM 集成&#xff0c;二者结合除了可以轻松搭建一个 LL…

在Discord上添加自己的服务器并邀请midjourney机器人加入

我开发的chatgpt网站&#xff1a; https://chat.xutongbao.top

vue2--多设备访问本地调试项目

背景 在vue2开发阶段&#xff0c;为了更好的和小伙伴对项目进行讨论&#xff0c;需要让小伙伴可以看到自己本地的项目。 方案 修改vue2项目配置 在本地进行项目调试时&#xff0c;都是使用的127.0.0.1:8080&#xff0c;为了让局域网中的其他小伙伴可以访问&#xff0c;需要…

阿里云国际-在阿里云服务器上快速搭建幻兽帕鲁多人服务器

幻兽帕鲁是最近流行的新型生存游戏。该游戏一夜之间变得极为流行&#xff0c;同时在线玩家数量达到了200万。然而&#xff0c;幻兽帕鲁的服务器难以应对大量玩家的压力。为解决这一问题&#xff0c;幻兽帕鲁允许玩家建立专用服务器&#xff0c;其提供以下优势&#xff1a; &am…

Docker中如何删除某个镜像

1. 停止使用镜像的容器 首先&#xff0c;您需要停止所有正在使用该镜像的容器。您可以使用 docker stop 命令来停止容器&#xff1a; docker stop 11184993a106如果有多个容器使用该镜像&#xff0c;您需要对每个容器都执行停止命令。您可以通过 docker ps -a | grep core-ba…

数据结构 计算结构体大小

一、规则&#xff1a; 操作系统制定对齐量&#xff1a; 64位操作系统&#xff0c;默认8Byte对齐 32位操作系统&#xff0c;默认4Byte对齐 结构体对齐规则&#xff1a; 1.结构体整体的大小&#xff0c;需要是最大成员对齐量的整数倍 2.结构体中每一个成员的偏移量需要存在…

Selenium常见问题解析

1、元素定位失败&#xff1a; 在使用Selenium自动化测试时&#xff0c;最常见的问题之一是无法正确地定位元素&#xff0c;这可能导致后续操作失败。解决方法包括使用不同的定位方式&#xff08;如xpath、CSS selector、id等&#xff09;&#xff0c;等待页面加载完全后再进行…

【Oracle】玩转Oracle数据库(三):数据库的创建和管理

前言 嘿&#xff0c;各位数据库小能手们&#xff01;今天我们要进入数据库的创世纪&#xff0c;探索Oracle数据库的创建和管理&#xff01;&#x1f527;&#x1f4bb; 在这篇博文【Oracle】玩转Oracle数据库&#xff08;三&#xff09;&#xff1a;数据库的创建和管理中&#…

详解动态内存管理!

目录 ​编辑 1.为什么要用动态内存分配 2.malloc和free 2.1 malloc 2.2 free 3.calloc和realloc 3.1 calloc 3.2 realloc 4.常见的动态内存的错误 4.1 对NULL的解引用操作 4.2 对动态内存开辟空间的越界访问 4.3 对非动态内存开辟空间用free释放 4.4 使用free释放动…

【北京游戏业:出海竞争实力全面】

本文将深入分析北京的游戏行业发展。在上海、广州、北京、深圳、成都、杭州、福建七大游戏产业中心城市中&#xff0c;北京无疑是出海竞争力最强的游戏产业集群。本文将全面剖析北京游戏行业的发展现状。 北京是中国游戏产业的发源地。拥有从游戏引擎到美术设计等完整的产业链…

如何在Shopee平台上选择爆款商品:借鉴爆款属性的有效策略

在当今激烈竞争的电商市场中&#xff0c;想要在Shopee平台上取得成功&#xff0c;卖家需要精心选择潜在的热销产品。借鉴爆款商品的属性是一种行之有效的策略&#xff0c;能够帮助卖家快速找到市场上的热门商品。通过分析市场趋势、竞品表现、社交媒体趋势等多方面因素&#xf…

【EI会议征稿通知】第四届自动化控制、算法与智能仿生学术会议(ACAIB 2024)

第四届自动化控制、算法与智能仿生学术会议(ACAIB 2024) 2024 4th Conference on Automation Control, Algorithm and Intelligent Bionics 第四届自动化控制、算法与智能仿生学术会议&#xff08;ACAIB 2024&#xff09;将于2024年6月7日-9日在中国银川举行。 本届大会由北…

leetcode hot100 完全平方数

本题中&#xff0c;是给一个整数n&#xff0c;让用完全平方数凑出这个整数&#xff0c;注意&#xff0c;题中给了n的范围&#xff0c;是大于等于1的&#xff0c;也就是说&#xff0c;dp[0]我们可以先不考虑。 整个问题可以抽象成完全背包问题的变形形式&#xff0c;物品就是这…

在 React 中使用 i18next支持多语言

基本用法 安装依赖包 npm i i18next react-i18next i18next-browser-languagedetector --savei18next 提供了翻译的基本能力; react-i18next 是 i18next 的一个插件&#xff0c;用来降低 react 的使用成本; i18next-browser-languagedetector 是用来检测浏览器语言的插件。 …

搜维尔科技:OptiTrack探索人类与技术之间关系的开创性表演

另一种蓝色通过 OptiTrack 释放创造力 总部位于荷兰的当代舞蹈团因其探索人类与技术之间关系的开创性表演而受到广泛赞誉。该公司由富有远见的编舞家大卫米登多普创立&#xff0c;不仅利用技术作为探索的主题&#xff0c;而且将其作为表达故事的动态工具。 “我一直对文化与…

【Java面试系列】JDK 1.8 新特性之 Stream API

目录 一、Stream 简介二、Stream 特点&#xff1a;Stream 注意点&#xff1a;1、什么是聚合操作2、Stream 流1、什么是流2、流的构成3、stream 流的两种操作4、惰性求值和及早求值方法5、Stream 流的并行 三、Stream操作的三个步骤1、创建流第一种&#xff1a;通过集合第二种&a…

ping 8.8.8.8和ping www.baidu.com都OK,但是打不开网页

ping 8.8.8.8和ping www.baidu.com都OK&#xff0c;但是打不开网页 打开设置 -> 网络 找到IPV4, DNS栏输入 8.8.8.8 , apply 设置里界面变成这样 然后网页就能加载了

mysql(五) buffer pool(缓存页数据与索引数据)

前面写了很多mysql的实用基础知识、接下来我将总结整理一些进阶和更深的一些知识、你知道的越多、不知道就越多、让我们一起学习。 目录 一、buffer pool的位置&#xff08;Innodb存储引擎内&#xff09; 二、Buffer Pool是什么&#xff1f; 1、降低磁盘访问的机制 2、Buf…

揭秘阿里巴巴商品详情API:电商数据获取新篇章,业务效率飞跃提升

阿里巴巴平台商品详情API接口技术深度解析 一、概览 在阿里巴巴这个庞大的电商生态中&#xff0c;商品详情API接口&#xff08;item_get&#xff09;扮演着至关重要的角色。通过这一接口&#xff0c;开发者可以轻松地获取商品的详细信息&#xff0c;为选品、数据分析、价格监…