广度优先遍历 订阅
宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。 展开全文
宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
信息
外文名
BFS
别    称
广度优先搜索
应用学科
计算机
中文名
宽度优先搜索算法
适用领域范围
计算机算法
宽度优先搜索概述
BFS,其英文全称是Breadth First Search。 BFS并不使用经验法则算法。从算法的观点,所有因为展开节点而得到的子节点都会被加进一个先进先出的队列中。一般的实验里,其邻居节点尚未被检验过的节点会被放置在一个被称为 open 的容器中(例如队列或是链表),而被检验过的节点则被放置在被称为 closed 的容器中。(open-closed表)
收起全文
精华内容
下载资源
问答
  • 本文实例讲述了python实现树的深度优先遍历与广度优先遍历。分享给大家供大家参考,具体如下: 广度优先(层次遍历) 从树的root开始,从上到下从左到右遍历整个树的节点 数和二叉树的区别就是,二叉树只有左右两个...
  • 主要介绍了JavaScript树的深度优先遍历和广度优先遍历算法,结合实例形式分析了JavaScript树的深度优先遍历、广度优先遍历递归与非递归相关实现技巧,需要的朋友可以参考下
  • 主要介绍了python实现树的深度优先遍历与广度优先遍历,详细分析了树的深度优先遍历与广度优先遍历原理及Python相关实现技巧,需要的朋友可以参考下
  • 深度优先遍历DFS 与树的先序遍历比较类似。 假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的...
  • js代码-二叉树广度优先遍历
  • 天津理工大学实验报告 学院系名称 计算机与通信工程学院 姓名 学号 专业 计算机科学与技术 班级 2009 级 1 班 实验项目 实验四 图的深度优先与广度优先遍历 课程名称 数据结构与算法 课程代码 实验时间 2011 年 5 月...
  • 天津理工大学实验报告 学院系名称 计算机与通信工程学院 姓名 学号 专业 计算机科学与技术 班级 2009 级 1 班 实验项目 实验四 图的深度优先与广度优先遍历 课程名称 数据结构与算法 课程代码 实验时间 2011 年 5 月...
  • - PAGE PAGE 2 欢迎下载 实验报告 学院系名称计算机与通信工程学院 姓名 学号 专业 计算机科学与技术 班级 实验项目 实验四图的深度优先与广度优先遍历 课程名称 数据结构与算法 课程代码 0661913 实验时间 2019年4...
  • C++实现,数据结构,图的邻接矩阵表示,深度优先遍历,广度优先遍历,DFS,BFS,为什么要五十个字才能上传啊
  • 主要介绍了Java实现利用广度优先遍历(BFS)计算最短路径的方法,实例分析了广度优先遍历算法的原理与使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
  • 广度优先遍历 实例

    2016-08-02 15:17:20
    广度优先遍历作为一个初学者必备的技能,此资源免费,广度优先遍历是连通图的一种遍历策略。因为它的思想是从一个顶点V0开始,辐射状地优先遍历其周围较广的区域,故得名
  • 主要介绍了Java实现二叉树的深度优先遍历和广度优先遍历算法,结合实例形式详细分析了二叉树的定义、深度优先遍历与广度优先遍历算法原理与相关操作实现技巧,需要的朋友可以参考下
  • 图的深度优先和广度优先遍历,下载下来可直接运行。你值得拥有
  • 深度优先遍历和广度优先遍历

    万次阅读 多人点赞 2019-05-29 21:24:38
    深度优先遍历和广度优先遍历 什么是 深度/广度 优先遍历? 深度优先遍历简称DFS(Depth First Search),广度优先遍历简称BFS(Breadth First Search),它们是遍历图当中所有顶点的两种方式。 这两...

    深度优先遍历和广度优先遍历
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    什么是 深度/广度 优先遍历?

    深度优先遍历简称DFS(Depth First Search),广度优先遍历简称BFS(Breadth First Search),它们是遍历图当中所有顶点的两种方式。

    这两种遍历方式有什么不同呢?我们来举个栗子:

    我们来到一个游乐场,游乐场里有11个景点。我们从景点0开始,要玩遍游乐场的所有景点,可以有什么样的游玩次序呢?

    在这里插入图片描述
    第一种是一头扎到底的玩法。我们选择一条支路,尽可能不断地深入,如果遇到死路就往回退,回退过程中如果遇到没探索过的支路,就进入该支路继续深入。

    在图中,我们首先选择景点1的这条路,继续深入到景点7、景点8,终于发现走不动了(景点旁边的数字代表探索次序):
    在这里插入图片描述
    于是,我们退回到景点7,然后探索景点10,又走到了死胡同。于是,退回到景点1,探索景点9:
    在这里插入图片描述
    按照这个思路,我们再退回到景点0,后续依次探索景点2、3、5、4、6,终于玩遍了整个游乐场:
    在这里插入图片描述
    像这样先深入探索,走到头再回退寻找其他出路的遍历方式,就叫做深度优先遍历(DFS)。
    在这里插入图片描述
    在这里插入图片描述
    除了像深度优先遍历这样一头扎到底的玩法以外,我们还有另一种玩法:首先把起点相邻的几个景点玩遍,然后去玩距离起点稍远一些(隔一层)的景点,然后再去玩距离起点更远一些(隔两层)的景点…

    在图中,我们首先探索景点0的相邻景点1、2、3、4:
    在这里插入图片描述
    接着,我们探索与景点0相隔一层的景点7、9、5、6:
    在这里插入图片描述
    最后,我们探索与景点0相隔两层的景点8、10:
    在这里插入图片描述
    像这样一层一层由内而外的遍历方式,就叫做广度优先遍历(BFS)。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    深度优先遍历

    首先说说深度优先遍历的实现过程。这里所说的回溯是什么意思呢?回溯顾名思义,就是自后向前,追溯曾经走过的路径。

    我们把刚才游乐场的例子抽象成数据结构的图,假如我们依次访问了顶点0、1、7、8,发现无路可走了,这时候我们要从顶点8退回到顶点7。

    在这里插入图片描述
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190529211147549.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2EyMjE3MDE4MTAz,size_16,color_FFFFFF,t_70

    而后我们探索了顶点10,又无路可走了,这时候我们要从顶点10退回到顶点7,再退回到顶点1。
    在这里插入图片描述
    像这样的自后向前追溯曾经访问过的路径,就叫做回溯。

    要想实现回溯,可以利用栈的先入后出特性,也可以采用递归的方式(因为递归本身就是基于方法调用栈来实现)。

    下面我们来演示一下具体实现过程。

    首先访问顶点0、1、7、8,这四个顶点依次入栈,此时顶点8是栈顶:
    在这里插入图片描述
    从顶点8退回到顶点7,顶点8出栈:
    在这里插入图片描述
    接下来访问顶点10,顶点10入栈:
    在这里插入图片描述
    从顶点10退到顶点7,从顶点7退到顶点1,顶点10和顶点7出栈:
    在这里插入图片描述
    探索顶点9,顶点9入栈:
    在这里插入图片描述
    以此类推,利用这样一个临时栈来实现回溯,最终遍历完所有顶点。

    广度优先遍历

    接下来该说说广度优先遍历的实现过程了。刚才所说的重放是什么意思呢?似乎听起来和回溯差不多?其实,回溯与重放是完全相反的过程。

    仍然以刚才的图为例,按照广度优先遍历的思想,我们首先遍历顶点0,然后遍历了邻近顶点1、2、3、4:

    在这里插入图片描述
    接下来我们要遍历更外围的顶点,可是如何找到这些更外围的顶点呢?我们需要把刚才遍历过的顶点1、2、3、4按顺序重新回顾一遍,从顶点1发现邻近的顶点7、9;从顶点3发现邻近的顶点5、6。
    在这里插入图片描述
    像这样把遍历过的顶点按照之前的遍历顺序重新回顾,就叫做重放。同样的,要实现重放也需要额外的存储空间,可以利用队列的先入先出特性来实现。

    下面我们来演示一下具体实现过程。
    首先遍历起点顶点0,顶点0入队:
    在这里插入图片描述
    接下来顶点0出队,遍历顶点0的邻近顶点1、2、3、4,并且把它们入队:
    在这里插入图片描述
    然后顶点1出队,遍历顶点1的邻近顶点7、9,并且把它们入队:
    在这里插入图片描述
    然后顶点2出队,没有新的顶点可入队:
    在这里插入图片描述
    以此类推,利用这样一个队列来实现重放,最终遍历完所有顶点。

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

    图的顶点
    
    / private static class Vertex { int data; Vertex(int data) {    this.data = data; } } /*
    
    图(邻接表形式)
    
    / private static class Graph { private int size; private Vertex[] vertexes; private LinkedList adj[]; Graph(int size){    this.size = size;    //初始化顶点和邻接矩阵    vertexes = new Vertex[size];    adj = new LinkedList[size];    for(int i=0; i*
    
    深度优先遍历
    
    / public static void dfs(Graph graph, int start, boolean[] visited) { System.out.println(graph.vertexes[start].data); visited[start] = true; for(int index : graph.adj[start]){    if(!visited[index]){        dfs(graph, index, visited);    } } } /*
    
    广度优先遍历
    
    */
    
    public static void bfs(Graph graph, int start, boolean[] visited, LinkedList queue) {
    
    queue.offer(start);
    
    while (!queue.isEmpty()){
    
       int front = queue.poll();
    
       if(visited[front]){
    
           continue;
    
       }
    
       System.out.println(graph.vertexes[front].data);
    
       visited[front] = true;
    
       for(int index : graph.adj[front]){
    
           queue.offer(index);;
    
       }
    
    }
    
    }
    
    public static void main(String[] args) {
    
    Graph graph = new Graph(6);
    
    graph.adj[0].add(1);
    
    graph.adj[0].add(2);
    
    graph.adj[0].add(3);
    
    graph.adj[1].add(0);
    
    graph.adj[1].add(3);
    
    graph.adj[1].add(4);
    
    graph.adj[2].add(0);
    
    graph.adj[3].add(0);
    
    graph.adj[3].add(1);
    
    graph.adj[3].add(4);
    
    graph.adj[3].add(5);
    
    graph.adj[4].add(1);
    
    graph.adj[4].add(3);
    
    graph.adj[4].add(5);
    
    graph.adj[5].add(3);
    
    graph.adj[5].add(4);
    
    System.out.println("图的深度优先遍历:");
    
    dfs(graph, 0, new boolean[graph.size]);
    
    System.out.println("图的广度优先遍历:");
    
    bfs(graph, 0, new boolean[graph.size], new LinkedList());
    
    }
    

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

    展开全文
  • C++ 实例之九宫格广度优先遍历 基本思路: 广度优先遍历,每次找到1的位置,分别向上、向下、向左、向右移动。把移动后的每个状态存储到队列中,弹出队头,判断是否为最终结果状态,如果是,输出遍历的层数(即移动...
  • 主要介绍了Java二叉搜索树遍历操作,结合实例形式详细分析了Java二叉搜索树前序、中序、后序、层次、广度优先遍历等相关原理与操作技巧,需要的朋友可以参考下
  • 主要介绍了PHP实现二叉树深度优先遍历(前序、中序、后序)和广度优先遍历(层次),结合实例形式详细分析了php针对二叉树的深度优先遍历与广度优先遍历相关操作技巧与注意事项,需要的朋友可以参考下
  • 本资源是用C语言所写的,数据结构中图的创建及其相关的深度,广度遍历
  • 邻接表存储图深度优先广度优先遍历
  • 主要介绍了C++非递归队列实现二叉树的广度优先遍历,实例分析了遍历二叉树相关算法技巧,并附带了两个相关算法实例,需要的朋友可以参考下
  • 本程序通过java使用邻接表对图进行深度和广度优先遍历,其中包括图和节点的数据结构实现
  • 图的引出:前面我们学了线性表和树。线性表局限于一个直接前驱和一个直接后继的关系;树也只能有一个直接前驱也就是父...2、广度优先遍历(Broad First Search)。 深度优先遍历思想:深度优先遍历是一种纵向切入..

    图的引出:前面我们学了线性表和树。线性表局限于一个直接前驱和一个直接后继的关系;树也只能有一个直接前驱也就是父结点。但我们需要表示多对多的关系时,我们就需要用到图这种数据结构。

    图的表示方式有两种:二维数组表示(邻接矩阵)链表表示(邻接表)

    邻接矩阵中,如果两个顶点相连通,则为1,如果不连通,则为零。

    图的遍历

    有两种策略。1、深度优先遍历(Depth First Search)。2、广度优先遍历(Broad First Search)

    深度优先遍历思想:深度优先遍历是一种纵向切入的思想。思想是先访问当前顶点,然后再以这个顶点作为初始顶点,访问该顶点的第一个邻接顶点。可以这样理解:每次访问完当前顶点后首先访问当前顶点的第一个邻接顶点。这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问。由此可以看出,需要通过递归来实现深度优先遍历

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

    接下来我举一个例子,讲述深度优先遍历的思想

    接下来还是以同样一个例子讲述广度优先的遍历思想:

     两者的区别:深度优先就是一直到底遍历;而广度优先就好似一张网,一步一步迈进遍历。

    这是两者最主要的区别

    深度优先遍历的步骤

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

    广度优先遍历的步骤

    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。

    具体实现代码附上,大家可以通过代码仔细去感悟。

    package com.liu.chart;
    
    import java.util.ArrayList;
    import java.util.LinkedList;
    
    
    /**
     * @author liuweixin
     * @create 2021-09-20 8:50
     */
    //图
    public class VertexChart {
        ArrayList<String> VertexList;
        int[][] edges;//用来储存顶点之间的关系
        int numOfEdges;//表示边的个数
    
        public static void main(String[] args) {
    //        String[] data = new String[]{"A", "B", "C", "D", "E"};
            String[] data = new String[]{"1", "2", "3", "4", "5", "6", "7", "8"};
            VertexChart vertexChart = new VertexChart(data.length);
            for (String value : data) {
                vertexChart.addVertex(value);
            }
    //        vertexChart.addEdge(0,1,1);
    //        vertexChart.addEdge(0,2,1);
    //        vertexChart.addEdge(1,2,1);
    //        vertexChart.addEdge(1,3,1);
    //        vertexChart.addEdge(1,4,1);
            vertexChart.addEdge(0, 1, 1);
            vertexChart.addEdge(0, 2, 1);
            vertexChart.addEdge(1, 3, 1);
            vertexChart.addEdge(1, 4, 1);
            vertexChart.addEdge(2, 5, 1);
            vertexChart.addEdge(2, 6, 1);
            vertexChart.addEdge(3, 7, 1);
            vertexChart.addEdge(4, 7, 1);
            vertexChart.addEdge(5, 6, 1);
            vertexChart.show();
            System.out.println();
            vertexChart.dfs();//深度优先遍历:1-2-4-8-5-3-6-7
            System.out.println();
            vertexChart.bfs();//广度优先遍历:1-2-3-4-5-6-7-8
        }
    
        public VertexChart(int VertexCount) {
            edges = new int[VertexCount][VertexCount];
            VertexList = new ArrayList<String>(VertexCount);
            numOfEdges = 0;
        }
    
        /**
         * 对广度优先方法的封装
         */
        public void bfs(){
            boolean[] isVisited = new boolean[VertexList.size()];
            //添加该循环的原因在于,有可能数据是分布在两张图甚至多张图上的,所以我们需要对所有的数据实现广度优先遍历
            //如果数据都分布在单一的一张图上,则不需要加此循环
            for (int i = 0; i < VertexList.size(); i++) {//对所有数据进行广度优先遍历
                if(!isVisited[i]){//判断是否访问过
                    bfs(isVisited,i);//进行广度优先遍历
                }
            }
        }
        /**
         *广度优先
         * @param isVisited 是否被访问
         * @param i  初始结点
         */
        public void bfs(boolean[] isVisited, int i){
            int u;//表示队列的头结点对应的下标
            int w;//邻接结点w
            //队列,记录结点访问的顺序
            LinkedList queue = new LinkedList();
            //访问结点,输出结点信息
            System.out.print(VertexList.get(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(VertexList.get(w)+" ");
                        //标记已经访问
                        isVisited[w]=true;
                        //入队
                        queue.addLast(w);
                    }
                    //以u为前驱点,找w后面的下一个邻接点
                    w=getNextNeighbor(u,w);//体现出广度优先.
                }
            }
        }
    
        //对深度优先遍历方法进行封装
        public void dfs() {
            boolean[] isVisited = new boolean[VertexList.size()];
            //添加该循环的原因在于,有可能数据是分布在两张图甚至多张图上的,所以我们需要对所有的数据实现深度优先遍历
            //如果数据都分布在单一的一张图上,则不需要加此循环
            for (int i = 0; i < VertexList.size(); i++) {//对所有数据进行深度优先遍历
                if(!isVisited[i]){//判断是否访问过
                    dfs(isVisited, 0);//进行深度优先
                }
            }
    
        }
    
        /**
         * 深度优先遍历
         *
         * @param isVisited 是否被访问
         * @param i         初始结点
         */
        public void dfs(boolean[] isVisited, int i) {
            System.out.print(VertexList.get(i) + " ");
            isVisited[i] = true;//设置该点已被访问过
            //获取该结点的第一个邻接结点的下标
            int index = getFirstNeighbor(i);
            while (index != -1) {//如果循环能进来,此时表明有下一个邻接结点
                if (!isVisited[index]) {//如果该结点未被访问过
                    //进行递归遍历+回溯
                    dfs(isVisited, index);
                }
                //如果该结点已被访问过
                index = getNextNeighbor(i, index);
            }
        }
    
        /**
         * 根据前一个结点的邻接结点的下标获取下一个邻接结点
         *
         * @param v1
         * @param v2
         * @return
         */
        public int getNextNeighbor(int v1, int v2) {
            for (int i = v2 + 1; i < VertexList.size(); i++) {
                if (edges[v1][i] > 0) {
                    return i;
                }
            }
            return -1;
        }
    
        /**
         * 得到该下标的第一个邻接结点
         *
         * @param index
         * @return 如果找到,则返回该下标值,如果找不到,返回-1
         */
        public int getFirstNeighbor(int index) {
            for (int i = 0; i < VertexList.size(); i++) {
                if (edges[index][i] == 1) {
                    return i;
                }
            }
            return -1;
        }
    
        /**
         * 显示图的邻接矩阵
         */
        public void show() {
            for (int[] arr : edges) {
                for (int data : arr) {
                    System.out.print(data + " ");
                }
                System.out.println();
            }
        }
    
        /**
         * 获取顶点的个数
         *
         * @return 返回顶点的个数
         */
        public int getVexCount() {
            return VertexList.size();
        }
    
        /**
         * 获取边的个数
         *
         * @return 返回边的个数
         */
        public int getNumOfEdges() {
            return numOfEdges;
        }
    
        /**
         * 返回对应的两个顶点之间的关系
         *
         * @param v1 第一个顶点的下标
         * @param v2 第二个顶点的下标
         * @return 如果返回1,则表示其可以连通;如果返回0,则表示其不连通
         */
        public int getWeight(int v1, int v2) {
            return edges[v1][v2];
        }
    
        /**
         * 获取对应下标的顶点值
         *
         * @param index 对应下标
         * @return 返回该顶点值
         */
        public String getVertex(int index) {
            return VertexList.get(index);
        }
    
        /**
         * 把顶点的数据存储到List中
         *
         * @param data
         */
        public void addVertex(String data) {
            VertexList.add(data);
        }
    
        /**
         * 将顶点之间的关系进行表示
         *
         * @param v1     表示点的下标,即第几个顶点
         * @param v2     第二个顶点对应的下边
         * @param weight 两者的关系。如果为1,则表示两者连通;如果为0,则表示两者不连通。
         */
        public void addEdge(int v1, int v2, int weight) {
            edges[v1][v2] = weight;
            edges[v2][v1] = weight;
            numOfEdges++;
        }
    }
    

    展开全文
  • 用邻接表存储表示图,完成了图的深度和广度优先遍历的操作。
  • 图的深度优先遍历和广度优先遍历-Java实现
  • 程序设计任务: 设计一个程序,实现以邻接表或者邻接矩阵为存储结构,实现连通无向图的深度优先和广度优先遍历。基本要求:以邻接表或者邻接矩阵为存储结构,实现连通无向图的深度优先和广度优先遍历。以用户指定的...
  • 邻接矩阵实现图,Java实现数据结构,Java实现图的深度优先遍历,广度优先遍历

    **

    Java实现数据结构,邻接矩阵实现图,Java实现图的深度、广度优先遍历

    **
    目录:

    1. 前言
    2. 深度预先遍历
    3. 广度优先遍历

    前言:
    首先这里主要讲Java实现图的深度和广度优先遍历,邻接矩阵实现图。
    深度优先和广度优先首先是不难,你之所以会来查找如何实现深度优先和广度优先,我觉得是你的深度广度的逻辑不懂,这里会告诉你深度广度的逻辑是什么。然后用代码于实现。
    示例图片
    图是无序图,A、B、C、D、E对应0、1、2、3、4
    **

    深度优先

    **:
    深度优先顾名思义就是:深是遍历的原则(深就是长驱直入),比如我们从A开始寻找,只要找到A的下一个邻接结点(就是和A相连的结点)就可以,找到的是B,再从B开始找B的下一个邻接结点找到的是C,在从C开始找C的邻接结点,(找过的就不在找),没有找到。这时我们说A的深度优先遍历就结束了,再开始B的深度优先遍历。
    深度优先简单来说,就是,长驱直入,不管有几个邻接结点,找到一个就行,找不到就从下一个结点开始深度优先遍历。
    代码实现:

    在这里插入代码片
    /**
     * 
     * @author 楠
     *
     */
    //图
    class graph{
    	public ArrayList<String> vertex;//存储结点的字符串数组
    	public int[][] edges;//邻接矩阵
    	public int numOfEdge;//边的个数
    	public boolean[] isVisited;//辅助遍历工具,用来判断一个结点是否被访问过
    	//构造器
    	public graph(int n) {//初始化图
    		vertex=new ArrayList<String>(n);
    		edges=new int[n][n];
    		numOfEdge=0;
    	}
    	//深度优先遍历图
    	public void dfs() {//对每一个结点都进行深度优先遍历
    		isVisited=new boolean[vertex.size()];//初始化辅助工具,初始均为未被访问
    		for(int i=0;i<vertex.size();i++) {
    			if(isVisited[i]) {//被访问就跳过
    				continue;
    			}else {
    				dfs(i);
    			}
    		}
    	}
    	private void dfs(int i) {//对结点i进行深度优先遍历
    		System.out.print(getValueOfVertex(i)+"---");
    		isVisited[i]=true;
    		int n=getFirst(i);//查找i的邻接结点
    		if(n!=-1) {//找到了
    			dfs(n);
    		}else {
    			return;
    		}
    	}
    	/**
    	 * 
    	 * @param i 给定结点的下标,
    	 * @return	返回结点i的第一个且没有被访问过的邻接结点,没有返回-1
    	 */
    	private int getFirst(int i) {
    		for(int j=0;j<vertex.size();j++) {
    			if(edges[i][j]>0&&!isVisited[j]) {
    				return j;
    			}
    		}
    		return -1;
    	}
    	
    	/*
    		一下都是图的基本操作
    	*/
    	
    	//添加结点
    	public void add(String str) {
    		vertex.add(str);
    	}
    	//添加边
    	public void addEdges(int a,int b,int value) {
    		edges[a][b]=value;
    		edges[b][a]=value;
    		numOfEdge++;
    	}
    	//返回结点的个数
    	public int getNumOfVertex() {
    		return vertex.size();
    	}
    	//根据结点返回权值
    	public int getValue(int a,int b) {
    		return edges[a][b];
    	}
    	//显示图对应的矩阵
    	public void show() {
    		for(int[] arr:edges) {
    			System.out.println(Arrays.toString(arr));
    		}
    	}
    	//返回边的数目
    	public int getNumEdges() {
    		return numOfEdge;
    	}
    	//返回下标对应的结点
    	public String getValueOfVertex(int a) {
    		return vertex.get(a);
    	}
    }
    
    

    **

    广度优先:

    **
    广度优先也是顾名思义,横向搜索的广度优先的原则,给一个结点i,找到结点i的所有邻接结点。再从找到的结点当中进行广度优先遍历。
    代码实现:

    在这里插
    /**
     * 
     * @author 楠
     *
     */
    //图
    class graph{
    	public ArrayList<String> vertex;//存储结点的字符串数组
    	public int[][] edges;//邻接矩阵
    	public int numOfEdge;//边的个数
    	public boolean[] isVisited;//辅助遍历工具,用来判断一个结点是否被访问过
    	//构造器
    	public graph(int n) {//初始化图
    		vertex=new ArrayList<String>(n);
    		edges=new int[n][n];
    		numOfEdge=0;
    	}
    	//广度优先遍历图
    	public void bfs() {//广度优先遍历只从一个遍历一次就可以了
    		isVisited=new boolean[vertex.size()];
    		bfs(0);
    		System.out.println();
    	}
    	private void bfs(int i) {
    		LinkedList<Integer> queue = new LinkedList<Integer>();//队列存储遍历出来的结点
    		System.out.print(getValueOfVertex(i)+"--");
    		queue.addLast(i);
    		isVisited[i]=true;	
    		while (!queue.isEmpty()) {//队列空的时候就遍历完了
    			int w=queue.removeFirst();
    			int n=getFirst(w);	
    			while (n!=-1) {//找到所有的邻接结点
    				queue.addLast(n);
    				System.out.print(getValueOfVertex(n)+"--");
    				isVisited[n]=true;
    				n=getFirst(w);
    			}
    		}
    		
    	}
    	/**
    	 * 
    	 * @param i 给定结点的下标,
    	 * @return	返回结点i的第一个且没有被访问过的邻接结点,没有返回-1
    	 */
    	private int getFirst(int i) {
    		for(int j=0;j<vertex.size();j++) {
    			if(edges[i][j]>0&&!isVisited[j]) {
    				return j;
    			}
    		}
    		return -1;
    	}
    	
    	/*
    		一下都是图的基本操作
    	*/
    	
    	//添加结点
    	public void add(String str) {
    		vertex.add(str);
    	}
    	//添加边
    	public void addEdges(int a,int b,int value) {
    		edges[a][b]=value;
    		edges[b][a]=value;
    		numOfEdge++;
    	}
    	//返回结点的个数
    	public int getNumOfVertex() {
    		return vertex.size();
    	}
    	//根据结点返回权值
    	public int getValue(int a,int b) {
    		return edges[a][b];
    	}
    	//显示图对应的矩阵
    	public void show() {
    		for(int[] arr:edges) {
    			System.out.println(Arrays.toString(arr));
    		}
    	}
    	//返回边的数目
    	public int getNumEdges() {
    		return numOfEdge;
    	}
    	//返回下标对应的结点
    	public String getValueOfVertex(int a) {
    		return vertex.get(a);
    	}
    }
    
    
    展开全文
  • 文章目录一、图的概述1.1 什么是图1.2 图对比线性表和树1.3 图的常见概念二、图的存储方式2.1 邻接矩阵2.2 邻接表三、图的...广度优先遍历3.2.1 什么是广度优先遍历3.2.2 广度优先遍历的步骤3.2.3 广度优先遍历代码实现...

    一、图的概述

    1.1 什么是图

    图(graph)是一种数据结构,它是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E)。其中 G 表示一个图,V 是图 G 中顶点的集合,E 是图 G 中边的集合。

    在这里插入图片描述

    从图的定义中我们可以注意到它的一个特点:图中数据元素被称为顶点(Vertex。而在线性表中的数据元素叫元素,树形结构中的数据元素叫做节点。

    1.2 图对比线性表和树

    前面学习过线性表和树,现在又学到了图。为什么有这么多的数据结构呢?它们各自有什么不可替代的特点呢?

    在线性表中,数据元素之间是串起来的,仅有线性关系,每个数据元素只有一个直接前驱和一个直接后继,可以理解为一对一。在树形结构中,数据元素之间有着明显的层次关系,并且每一层上的数据元素可能和下一层中的多个元素相关,但是只能和上一层中一个元素相关,可以理解为一对多

    图示一种较线性表和树更加复杂的数据结构。在图形结构中,节点之间的关系可以是任意的,图中任意两个数据元素之间都可能相关,可以理解为多对多

    1.3 图的常见概念

    在图中有比较多的概念需要掌握,下面用一个表格来展示图形结构的常见概念。

    常见概念概念解释
    顶点数据元素
    两个顶点之间的连线
    路径从顶点 A 到顶点 B 要经过的边(路径可以有多条)
    邻接点相邻的顶点(如 A 和 B 通过一条边直接相连,则 B 是 A 的邻接点,A 是 B 的邻接点)
    无向图所有的边都没有方向
    有向图所有的边都有方向(如 A->B,表示只能从 A 到 B,无法从 B 到 A)
    带权图所有的边都带有权值
    连通图任意两个顶点都是连通的

    在这里插入图片描述

    二、图的存储方式

    图的存储方式有两种:二维数组表示(邻接矩阵)、链表表示(邻接表)。

    2.1 邻接矩阵

    图的邻接矩阵 (Adjacency Matrix) 存储方式是用两个数组来表示图,其中一个一维数组用来存储顶点信息,一个二维数组(称为邻接矩阵)存储图中的边的信息。

    邻接矩阵的行和列都是代表着图的第几个顶点。

    在这里插入图片描述

    如上所示图左是一个无向图,右边是该无向图的邻接矩阵:顶点 0 和顶点 1 之间存在一条边,因此 arr[0][1]arr[1][0] 都为 1(仅适用于无向图);顶点 0 和顶点 5 之间没有直接相连的边,因此 arr[0][5]arr[5][0] 都是 0。

    【案例演示】

    代码实现如下图结构,要求使用邻接矩阵。

    在这里插入图片描述

    public class No1_Graph {
        public static void main(String[] args) {
            String[] str = {"A", "B", "C", "D", "E"};
            // 创建一个图对象
            Graph graph = new Graph(str.length);
            // 添加顶点
            for (String vertex : str){
                graph.addVertex(vertex);
            }
            // 设置顶点之间的边
            graph.setEdges(0, 1, 1);    // A<->B
            graph.setEdges(1, 2, 1);    // B<->C
            graph.setEdges(1, 3, 1);    // B<->D
            graph.setEdges(1, 4, 1);    // B<->E;
            graph.setEdges(0, 2, 1);    // A<->C;
        }
    }
    
    /**
     * 图
     */
    class Graph{
        private ArrayList<String> vertexList;   // 存储顶点的集合
        private int[][] edges;  // 图对应的邻接矩阵
        private int numOfEdges; // 顶点之间边的个数
    	
        // 图的初始化
        public Graph(int n){
            this.vertexList = new ArrayList<>();
            this.edges = new int[n][n];
            this.numOfEdges = 0;
            this.isVisited = new boolean[n];
        }
        // 添加顶点到图中
        public void addVertex(String vertex){
            vertexList.add(vertex);
        }
        /**
         * 设置两个顶点之间的边
         * @param n 第一个顶点的索引
         * @param m 第二个顶点的索引
         * @param weight    边的权重
         * @return void
         */
        public void setEdges(int n, int m, int weight){
            edges[n][m] = weight;
            edges[m][n] = weight;
            numOfEdges++;
        }
        // 打印邻接矩阵
        public void printGraph(){
            for (int[] edge : edges){
                System.out.println(Arrays.toString(edge));
            }
        }
    }
    

    代码运行结果如下:

    在这里插入图片描述

    2.2 邻接表

    图的邻接表(Adjacency List)存储方式是用数组和链表来表示图,其中一个一维数组用来存储顶点,顶点数组中的每个数据元素还需要存储指向该顶点第一个邻接点的指针,每个顶点的所有邻接点构成一个线性表。

    邻接矩阵需要为每个顶点都分配 n 个边的空间,其实有很多边都是不存在,会造成空间的一定损失。邻接表的实现只关心存在的边,不关心不存在的边,因此没有空间浪费。

    如下图所示就是一个无向图的邻接表结构。

    在这里插入图片描述

    如图右所示,是无向图的邻接表:如标号为 0 的顶点的邻接点为 1、2、3、4;标号为 2 的订单的邻接点为 0、5。

    顶点数组中的数据元素由两部分组成,一部分是数据域存储顶点的信息,另一部分是指针域,指向该顶点的第一个邻接点。链表节点同样由两部分组成,一部分是邻接点域,用于存储与顶点相邻的点在图中的位置,另一部分是指针域,指向下一条边(如果边还有权值,还需要一个数据域用于存储权值,共三部分)。

    三、图的遍历

    从图中某一个顶点出发遍历图中其余顶点,且使每一个顶点仅被访问一次,这个过程就叫做图的遍历。一个图有多个顶点,如何遍历这些顶点,需要特定策略,一般有两种访问策略:深度优先遍历、广度优先遍历。

    3.1 图的深度优先遍历

    3.1.1 什么是深度优先遍历

    深度优先遍历(Depth First Search),也称深度优先搜索,简称为 DFS。

    深度优先遍历的思想说起来可能比较抽象,下面举个例子说一下。

    古时候有个功夫平平的剑客,一直想找到武林秘籍来提升自己的功夫。也算他命好,在行走江湖的路上他认识了一位神秘的老者,老者看他心系武林,就把守护了一生的秘密告诉他:在黄山、华山、泰山、峨眉山中的其中一座山里藏着失传已经的降龙十八掌,但是具体在哪座山需要他自己去找。

    一听说这个,剑客可就不困了。但是冷静下来之后,这个剑客有点懵逼了,这几座山这么大,离得还这么远,该怎么去寻找武林秘籍呢?

    经过一夜的辗转反侧,剑客最终确定了行动思路:先去黄山,不放过黄山的任何一个角落,把黄山的所有山洞、所有的小溪、所有的山路、所有的树木甚至是动物都找个遍,简单来说就是翻个底朝天,然后再去下一座山,再翻个底朝天,直到找到为止。

    这就是深度优先遍历的思想,简单来说就是优先向纵深挖掘,它类似于树的先序遍历的过程。

    3.1.2 深度优先遍历的步骤

    假设初始状态是图中所有顶点未曾被访问,则深度优先搜索可从图中某个顶点 v 出发,访问此顶点,然后依次从 v 的未被访问的邻接点出发深度优先遍历图,直至图中所有和 v 有路径相通的顶点都被访问到;若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

    以下面一个无向图为例,讲解其深度优先遍历过程。

    在这里插入图片描述

    假设从顶点 v1 出发,在访问完 v1 之后,选择邻接点 v2,因为 v2 未被访问,则从 v2 出发进行搜索。依次类推,接着从 v4、v7 出发进行搜索。在访问了 v7 之后,由于 v7 的邻接点都已经被访问,则搜索退回到 v4。由于同样的理由搜索继续回到 v2,直至 v1。此时由于 v1 的另一个邻接点未被访问,则搜索又从 v1 到 v3,再继续进行下去。由此,得到的顶点访问序列为:
    v 1 → v 2 → v 4 → v 7 → v 3 → v 5 → v 6 v1→v2→v4→v7→v3→v5→v6 v1v2v4v7v3v5v6
    显然,这是一个递归加回溯的过程。为了在遍历过程中便于区分顶点是否已被访问,需附设访问标志数组 isVisited[0.. n-1],其初始值为false,一旦某个顶点被访问,则其相应的分量置为 true

    3.1.3 深度优先遍历代码实现

    boolean[] isVisited = new boolean[vertexList.size];	// 标志数组
    
    /**
     * 从顶点 v 开始深度优先搜索 (DFS)
     * @param v 顶点
     */
    public void dfs(int v){
        isVisited[v] = true;    // 设置当前顶点状态为已访问
        System.out.print(vertexList.get(v) + "->");     // 打印当前顶点
        for (int i=0; i<vertexList.size(); i++){	// 遍历顶点	
            if (edges[v][i]==1 && !isVisited[i]){   // 如果某个顶点和当前顶点有直连的边且该顶点未被访问过
                dfs(i); // 递归深度优先遍历这个顶点
            }
        }
    }
    
    /**
     * 对图深度优先搜索
     */
    public void dfsTraverse(){
        /* 有可能是非连通图,出现顶点独立现象,因此有可能要以每个顶点为起点都来一次深度优先搜索 */
        for (int i=0; i<vertexList.size(); i++){
            if (!isVisited[i]){ // 如果顶点未被访问过,则从它开始 dfs。对于连通图,只会执行一次。
                dfs(i);
            }
        }
    }
    

    3.2 图的广度优先遍历

    3.2.1 什么是广度优先遍历

    广度优先遍历(Breadth First Search),又称为广度优先搜索,简称 BFS。

    还是以剑客找武林秘籍为例。剑客在赶往黄山的路上一想到要把整座山都翻个遍,瞬间头皮就麻了,他觉得武林秘籍总不至于会藏在山上的某个小石头下面吧,一定是藏在某个有灵气的地方。于是他改变了策略:先到每座山上大概瞅一眼,看看山的山势、灵气是不是像有武林秘籍的地方,如果都没有,再依次到各个山的山洞、小溪中找一找,如果没找到,再依次到各个山的小石头下翻一翻…,直到找到为止。

    这就是广度优先遍历的思想,它类似于树的按层次遍历的过程。

    3.2.2 广度优先遍历的步骤

    假设从图中顶点 v 出发,在 访问了 v 之后依次访问 v 的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使 “先被访问的顶点的邻接点” 先于 “后被访问的顶点的邻接点” 被访问,直至图中所有已被访问的顶点的邻接点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

    还是以下面一个无向图为例,讲解广度优先遍历过程。

    在这里插入图片描述

    首先访问 v1 和 v1 的邻接点 v2 和 v3,然后依次访问 v2 的邻接点 v4 和 v7 以及 v3 的邻接点 v5 和 v6。由于这些顶点的邻接点均已被访问,并且图中所有的顶点都被访问,由此完成了图的遍历。最终得到的顶点访问序列为:
    v 1 → v 2 → v 3 → v 4 → v 7 → v 5 → v 6 v1→v2→v3→v4→v7→v5→v6 v1v2v3v4v7v5v6
    和深度优先遍历类似,广度优先遍历在遍历的过程中也需要一个访问标志数组。并且,为了顺次访问路径长度为 2、3 … 的顶点,需要附设队列以存储已被访问的路径长度为 1、2 … 的顶点。

    3.2.3 广度优先遍历代码实现

    /**
     * 从顶点 v 开始广度优先遍历 (BFS)
     * @param v 顶点
     */
    public void bfs(int v){
        LinkedList<Integer> queue = new LinkedList<>(); // 队列,用于存放已被访问的顶点
        isVisited[v] = true;    // 设置状态为已访问
        System.out.print(vertexList.get(v) + "->"); // 打印顶点
        queue.addLast(v);	// 添加当前顶点到队列中
    
        while (!queue.isEmpty()){   // 如果当前队列不为空
            v = queue.removeFirst();   // 队头元素出队列,接下来将以这个出队列的元素为起点访问邻接点
            for (int i=0; i<vertexList.size(); i++){
                if (edges[v][i] == 1 && !isVisited[i]){ // 判断其它顶点与当前顶点是否有边且是否访问过
                    isVisited[i] = true;    // 标记符合条件的顶点状态为已访问
                    System.out.print(vertexList.get(i) + "->"); // 打印顶点
                    queue.addLast(i);   // 将此顶点入队列
                }
            }
        }
    }
    
    /**
     * 广度优先遍历图
     */
    public void bfsTraverse(){
        /* 可能是非连通图,出现顶点独立现象,因此有可能每个顶点都要广度优先搜索 */
        for (int i=0; i<vertexList.size(); i++){
            if (!isVisited[i]){ // 如果顶点未被访问过,则对该顶点广度优先搜索,对于连通图只会执行一次
                bfs(i);
            }
        }
    }
    
    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 50,095
精华内容 20,038
关键字:

广度优先遍历