深度优先搜索(DFS)与广度优先搜索(BFS)的Java实现
基础部分
在图中实现最基本的操作之一就是搜索从一个指定顶点可以到达哪些顶点,比如从武汉出发的高铁可以到达哪些城市,一些城市可以直达,一些城市不能直达。现在有一份全国高铁模拟图,要从某个城市(顶点)开始,沿着铁轨(边)移动到其他城市(顶点),有两种方法可以用来搜索图:深度优先搜索(DFS)和广度优先搜索(BFS)。它们最终都会到达所有连通的顶点,深度优先搜索通过栈来实现,而广度优先搜索通过队列来实现,不同的实现机制导致不同的搜索方式。
深度优先搜索
深度优先搜索算法有如下规则:
规则1:如果可能,访问一个邻接的未访问顶点,标记它,并将它放入栈中。
规则2:当不能执行规则 1 时,如果栈不为空,就从栈中弹出一个顶点。
规则3:如果不能执行规则 1 和规则 2 时,就完成了整个搜索过程。
对于上图,应用深度优先搜索如下:假设选取 A 顶点为起始点,并且按照字母优先顺序进行访问,那么应用规则 1 ,接下来访问顶点 B,然后标记它,并将它放入栈中;再次应用规则 1,接下来访问顶点 F,再次应用规则 1,访问顶点 H。我们这时候发现,没有 H 顶点的邻接点了,这时候应用规则 2,从栈中弹出 H,这时候回到了顶点 F,但是我们发现 F 也除了 H 也没有与之邻接且未访问的顶点了,那么再弹出 F,这时候回到顶点 B,同理规则 1 应用不了,应用规则 2,弹出 B,这时候栈中只有顶点 A了,然后 A 还有未访问的邻接点,所有接下来访问顶点 C,但是 C又是这条线的终点,所以从栈中弹出它,再次回到 A,接着访问 D,G,I,最后也回到了 A,然后访问 E,但是最后又回到了顶点 A,这时候我们发现 A没有未访问的邻接点了,所以也把它弹出栈。现在栈中已无顶点,于是应用规则 3,完成了整个搜索过程。
深度优先搜索在于能够找到与某一顶点邻接且没有访问过的顶点。这里以邻接矩阵为例,找到顶点所在的行,从第一列开始向后寻找值为1的列;列号是邻接顶点的号码,检查这个顶点是否未访问过,如果是这样,那么这就是要访问的下一个顶点,如果该行没有顶点既等于1(邻接)且又是未访问的,那么与指定点相邻接的顶点就全部访问过了(后面会用算法实现)。
广度优先搜索
深度优先搜索要尽可能的远离起始点,而广度优先搜索则要尽可能的靠近起始点,它首先访问起始顶点的所有邻接点,然后再访问较远的区域,这种搜索不能用栈实现,而是用队列实现。
规则1:访问下一个未访问的邻接点(如果存在),这个顶点必须是当前顶点的邻接点,标记它,并把它插入到队列中。
规则2:如果已经没有未访问的邻接点而不能执行规则 1 时,那么从队列列头取出一个顶点(如果存在),并使其成为当前顶点。
规则3:如果因为队列为空而不能执行规则 2,则搜索结束。
对于上面的图,应用广度优先搜索:以A为起始点,首先访问所有与 A 相邻的顶点,并在访问的同时将其插入队列中,现在已经访问了 A,B,C,D和E。这时队列(从头到尾)包含 BCDE,已经没有未访问的且与顶点 A 邻接的顶点了,所以从队列中取出B,寻找与B邻接的顶点,这时找到F,所以把F插入到队列中。已经没有未访问且与B邻接的顶点了,所以从队列列头取出C,它没有未访问的邻接点。因此取出 D 并访问 G,D也没有未访问的邻接点了,所以取出E,现在队列中有 FG,在取出 F,访问 H,然后取出 G,访问 I,现在队列中有 HI,当取出他们时,发现没有其它为访问的顶点了,这时队列为空,搜索结束。
代码实现
实现深度优先搜索的栈 StackX.class:
- package testOffer.graphpro;
- //实现深度优先搜索的栈
- public class StackX {
- private final int SIZE = 20;
- private int[] st;
- private int top;
- public StackX(){
- st = new int[SIZE];
- top = -1;
- }
- public void push(int j){
- st[++top] = j;
- }
- public int pop(){
- return st[top--];
- }
- public int peek(){
- return st[top];
- }
- public boolean isEmpty(){
- return (top==-1);
- }
- }
实现广度优先搜索的队列Queue.class:
- package testOffer.graphpro;
- //实现广度优先搜索的队列
- public class QueueX {
- private final int SIZE = 20;
- private int[] queArray;
- private int front;
- private int rear;
- public QueueX(){
- queArray = new int[SIZE];
- front = 0;
- rear = -1;
- }
- public void insert(int j){
- if (rear == SIZE-1)
- rear = -1;
- queArray[++rear] = j;
- }
- public int remove(){
- int temp = queArray[front++];
- if (front == SIZE){
- front = 0;
- }
- return temp;
- }
- public boolean isEmpty(){
- return (rear+1 == front || front+SIZE-1 == rear);
- }
- }
图代码 Graph.class:
- package testOffer.graphpro;
- public class Graph {
- private final int MAX_VERTS = 20;//表示顶点的个数
- private Vertex vertexList[];//用来存储顶点的数组
- private int adjMat[][];//用邻接矩阵来存储边,数组元素表示没有边界,1表示有边界
- private int nVerts;//顶点个数
- private StackX theStack;//用栈实现深度优先搜索
- private QueueX queue;//用队列实现广度优先搜索
- /**
- * 顶点类
- * */
- class Vertex{
- public char label;
- public boolean wasVisited;
- public Vertex(char label){
- this.label = label;
- wasVisited = false;
- }
- }
- public Graph(){
- vertexList = new Vertex[MAX_VERTS];
- adjMat = new int[MAX_VERTS][MAX_VERTS];
- nVerts = 0;//初始化顶点个数为0
- //初始化邻接矩阵所有元素都为0,即所有顶点都没有边
- for (int i=0;i<MAX_VERTS;i++){
- for (int j=0;j<MAX_VERTS;j++){
- adjMat[i][j] = 0;
- }
- }
- theStack = new StackX();
- queue = new QueueX();
- }
- //将顶点添加到数组中,是否访问标志置为wasVisited=false(未访问)、
- public void addVertex(char lab){
- vertexList[nVerts++] = new Vertex(lab);
- }
- //注意用邻接矩阵表示边,是对称的,两部分都要赋值
- public void addEdge(int start,int end){
- adjMat[start][end] = 1;
- adjMat[end][start] = 1;
- }
- //打印某个顶点表示的值
- public void displayVertex(int v){
- System.out.print(vertexList[v].label+" ");
- }
- /**深度优先搜索算法
- * 1、用peek方法检查栈顶的顶点
- * 2、用getAdjUnvisitedVertex方法找到当前栈顶邻接且未被访问的顶点
- * 3、第二步返回值不等于-1则找到下一个未访问的邻接顶点,访问这个顶点,并入栈
- * 如第二步返回值等于-1,则没有找到,出栈
- * */
- public void depthFirstSearch(){
- //从第一个顶点开始访问
- vertexList[0].wasVisited = true;//访问之后标记为true
- displayVertex(0);
- theStack.push(0);
- while (!theStack.isEmpty()){
- //找到栈当前顶点邻接且未被访问的顶点
- int v = getAdjUnvisitedVertex(theStack.peek());
- if (v==-1){//如果当前顶点值为-1,则表示没有邻接且未被访问的顶点,那么出栈顶点
- theStack.pop();
- }else {
- //否则访问下一个邻接点
- vertexList[v].wasVisited = true;
- displayVertex(v);
- theStack.push(v);
- }
- }
- //栈访问完毕,重置所有标记位为false
- for (int i=0;i<nVerts;i++){
- vertexList[i].wasVisited = false;
- }
- }
- //找到与某一顶点邻接且未被访问的顶点
- public int getAdjUnvisitedVertex(int v){
- for (int i=0;i<nVerts;i++){
- //v顶点与i顶点相邻且未被访问
- if (adjMat[v][i]==1&&vertexList[i].wasVisited==false)
- return i;
- }
- return -1;
- }
- /**广度优先搜索
- * 1、用remove方法检查栈顶的栈顶
- * 2、试图找到这个顶点还未被访问的邻接点
- * 3、如果没有找到,该顶点出列
- * 4、如果找到这样的顶点,访问这个顶点,并把它放入队列中
- * */
- public void breadthFirstSearch(){
- vertexList[0].wasVisited = true;
- displayVertex(0);
- queue.insert(0);
- int v2;
- while(!queue.isEmpty()){
- int v1 = queue.remove();
- while ((v2 =getAdjUnvisitedVertex(v1))!=-1){
- vertexList[v2].wasVisited = true;
- displayVertex(v2);
- queue.insert(v2);
- }
- }
- //搜索完毕,初始化,便于下次搜索
- for (int i=0;i<nVerts;i++){
- vertexList[i].wasVisited = false;
- }
- }
- public static void main(String[] args) {
- Graph graph = new Graph();
- graph.addVertex('A');
- graph.addVertex('B');
- graph.addVertex('C');
- graph.addVertex('D');
- graph.addVertex('E');
- graph.addEdge(0,1);//AB
- graph.addEdge(1,2);//BC
- graph.addEdge(0,3);//AD
- graph.addEdge(3,4);//DE
- System.out.println("深度优先搜索算法:");
- graph.depthFirstSearch();
- System.out.println();
- System.out.println("---------------");
- System.out.println("广度优先搜索算法:");
- graph.breadthFirstSearch();
- }
- }
深度优先搜索(DFS)与广度优先搜索(BFS)的Java实现的更多相关文章
- 深度优先搜索DFS和广度优先搜索BFS简单解析(新手向)
深度优先搜索DFS和广度优先搜索BFS简单解析 与树的遍历类似,图的遍历要求从某一点出发,每个点仅被访问一次,这个过程就是图的遍历.图的遍历常用的有深度优先搜索和广度优先搜索,这两者对于有向图和无向图 ...
- 深度优先搜索DFS和广度优先搜索BFS简单解析
转自:https://www.cnblogs.com/FZfangzheng/p/8529132.html 深度优先搜索DFS和广度优先搜索BFS简单解析 与树的遍历类似,图的遍历要求从某一点出发,每 ...
- 图的遍历(搜索)算法(深度优先算法DFS和广度优先算法BFS)
图的遍历的定义: 从图的某个顶点出发访问遍图中所有顶点,且每个顶点仅被访问一次.(连通图与非连通图) 深度优先遍历(DFS): 1.访问指定的起始顶点: 2.若当前访问的顶点的邻接顶点有未被访问的,则 ...
- 深度优先搜索DFS和广度优先搜索BFS
DFS简介 深度优先搜索,一般会设置一个数组visited记录每个顶点的访问状态,初始状态图中所有顶点均未被访问,从某个未被访问过的顶点开始按照某个原则一直往深处访问,访问的过程中随时更新数组visi ...
- 图的深度优先搜索(DFS)和广度优先搜索(BFS)算法
深度优先(DFS) 深度优先遍历,从初始访问结点出发,我们知道初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接 ...
- 图的 储存 深度优先(DFS)广度优先(BFS)遍历
图遍历的概念: 从图中某顶点出发访遍图中每个顶点,且每个顶点仅访问一次,此过程称为图的遍历(Traversing Graph).图的遍历算法是求解图的连通性问题.拓扑排序和求关键路径等算法的基础.图的 ...
- 图的深度优先遍历(DFS)和广度优先遍历(BFS)
body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...
- 【C++】基于邻接矩阵的图的深度优先遍历(DFS)和广度优先遍历(BFS)
写在前面:本博客为本人原创,严禁任何形式的转载!本博客只允许放在博客园(.cnblogs.com),如果您在其他网站看到这篇博文,请通过下面这个唯一的合法链接转到原文! 本博客全网唯一合法URL:ht ...
- 图的深度优先遍历(DFS)和广度优先遍历(BFS)算法分析
1. 深度优先遍历 深度优先遍历(Depth First Search)的主要思想是: 1.首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点: 2.当没有未访问过的顶点时,则回 ...
- 深度优先搜索(DFS)和广度优先搜索(BFS)
深度优先搜索(DFS) 广度优先搜索(BFS) 1.介绍 广度优先搜索(BFS)是图的另一种遍历方式,与DFS相对,是以广度优先进行搜索.简言之就是先访问图的顶点,然后广度优先访问其邻接点,然后再依次 ...
随机推荐
- 俗话:MySQL索引
MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所谓“好马配好鞍”,如何能够更好的使用它,已经成为开发工程师的必修课,我们经常会从职位 ...
- php对接微信小程序支付
前言:这里我就假装你已经注册了微信小程序,并且基本的配置都已经好了.注: 个人注册小程序不支持微信支付,所以我还是假装你是企业或者个体工商户的微信小程序,其他的商户号注册,二者绑定,授权,支付开通,就 ...
- ASP.NET Core 2 学习笔记(一)
来势汹汹的.NET Core似乎要取代.NET Framework,ASP.NET也随之发布.NET Core版本.虽然名称沿用ASP.NET,但相对于ASP.NET确实有许多架构上的差异,可以说除了 ...
- 4. 整合MyBatis
mybatis既有jdbc的灵活,有具有orm工具的方便,是一套很好用的工具,这儿就使用mybatis来作为数据访问工具,具体添加过程如下: 1. 添加mybatis依赖,并更新项目 <depe ...
- xp开机黑屏故障分析
今天装完xp系统之后,重启开机发现竟然黑屏了,查资料发现有很多用户在修改分辨率后,因显示器不支持修改后的分辨率,会出现电脑黑屏的情况.分辨率调高了,超出了屏幕的范围,肯定会黑屏,而且这个问题还挺麻烦, ...
- SPU和SKU有什么区别
SPU = Standard Product Unit (标准产品单位)SPU是商品信息聚合的最小单位,是一组可复用.易检索的标准化信息的集合,该集合描述了一个产品的特性.通俗点讲,属性值.特性相同的 ...
- bzoj 2500 幸福的道路 树上直径+set
首先明确:树上任意一点的最长路径一定是直径的某一端点. 所以先找出直径,求出最长路径,然后再求波动值<=m的最长区间 #include<cstdio> #include<cst ...
- LOJ_6045_「雅礼集训 2017 Day8」价 _最小割
LOJ_6045_「雅礼集训 2017 Day8」价 _最小割 描述: 有$n$种减肥药,$n$种药材,每种减肥药有一些对应的药材和一个收益. 假设选择吃下$K$种减肥药,那么需要这$K$种减肥药包含 ...
- BZOJ_2734_[HNOI2012]集合选数_构造+状压DP
BZOJ_2734_[HNOI2012]集合选数_构造+状压DP 题意:<集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x ...
- (1)STM32使用HAL库操作GPIO
一 初始化GPIO 使用HAL库的优点在于不用手动添加初始化的代码了,CubeMX会根据软件设置自动生成. 自动生成的HAL库GPIO初始化代码: static void MX_GPIO_Init( ...