精华内容
下载资源
问答
  • 深度优先遍历和广度优先遍历

    万次阅读 多人点赞 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());
    
    }
    

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

    展开全文
  • 广度优先搜索算法

    万次阅读 多人点赞 2019-04-25 13:26:58
    广度优先搜索算法(Breadth-First Search,BFS)是一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。BFS并不使用经验...

    一、简介

    广度优先搜索算法(Breadth-First Search,BFS)是一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。BFS并不使用经验法则算法。

    广度优先搜索让你能够找出两样东西之间的最短距离,不过最短距离的含义有很多!使用广度优先搜索可以:

    1. 编写国际跳棋AI,计算最少走多少步就可获胜;
    2. 编写拼写检查器,计算最少编辑多少个地方就可将错拼的单词改成正确的单词,如将READED改为READER需要编辑一个地方;
    3. 根据你的人际关系网络找到关系最近的医生。

    二、例子

    假设你居住在旧金山,要从双子峰前往金门大桥。你想乘公交车前往,并希望换乘最少。可乘坐的公交车如下。

    为找出换乘最少的乘车路线,你将使用什么样的算法?
    一步就能到达金门大桥吗?下面突出了所有一步就能到达的地方。

    金门大桥未突出,因此一步无法到达那里。两步能吗?

    金门大桥也未突出,因此两步也到不了。三步呢?

    金门大桥突出了!因此从双子峰出发,可沿下面的路线三步到达金门大桥。

     还有其他前往金门大桥的路线,但它们更远(需要四步)。这个算法发现,前往金门大桥的最短路径需要三步。这种问题被称为最短路径问题(shorterst-path problem)。你经常要找出最短路径,这可能是前往朋友家的最短路径,也可能是国际象棋中把对方将死的最少步数。解决最短路径问题的算法被称为广度优先搜索。要确定如何从双子峰前往金门大桥,需要两个步骤。
    (1) 使用图来建立问题模型。
    (2) 使用广度优先搜索解决问题。
    下面介绍什么是图,然后再详细探讨广度优先搜索。

    三、图

    图是由顶点的有穷非空集合和顶点之间边的集合组成,通过表示为G(V,E),其中,G标示一个图,V是图G中顶点的集合,E是图G中边的集合。

    无边图:若顶点Vi到Vj之间的边没有方向,则称这条边为无项边(Edge),用序偶对(Vi,Vj)标示。

    对于下图无向图G1来说,G1=(V1, {E1}),其中顶点集合V1={A,B,C,D};边集合E1={(A,B),(B,C),(C,D),(D,A),(A,C)}:

    有向图:若从顶点Vi到Vj的边是有方向的,则成这条边为有向边,也称为弧(Arc)。用有序对(Vi,Vj)标示,Vi称为弧尾,Vj称为弧头。如果任意两条边之间都是有向的,则称该图为有向图。

    有向图G2中,G2=(V2,{E2}),顶点集合(A,B,C,D),弧集合E2={<A,D>,{B,A},<C,A>,<B,C>}.

    权:有些图的边和弧有相关的数,这个数叫做权。这些带权的图通常称为网。

    四、广度优先搜索算法

    假设你经营着一个芒果农场,需要寻找芒果销售商,以便将芒果卖给他。在Facebook,你与芒果销售商有联系吗?为此,你可在朋友中查找。

    这种查找很简单。首先,创建一个朋友名单。

     然后,依次检查名单中的每个人,看看他是否是芒果销售商。

     假设你没有朋友是芒果销售商,那么你就必须在朋友的朋友中查找。

     检查名单中的每个人时,你都将其朋友加入名单。

     这样一来,你不仅在朋友中查找,还在朋友的朋友中查找。别忘了,你的目标是在你的人际关系网中找到一位芒果销售商。因此,如果Alice不是芒果销售商,就将其朋友也加入到名单中。这意味着你将在她的朋友、朋友的朋友等中查找。使用这种算法将搜遍你的整个人际关系网,直到找到芒果销售商。这就是广度优先搜索算法。

    五、查找最短路径

    再说一次,广度优先搜索可回答两类问题。
    第一类问题:从节点A出发,有前往节点B的路径吗?(在你的人际关系网中,有芒果销售商吗?)
    第二类问题:从节点A出发,前往节点B的哪条路径最短?(哪个芒果销售商与你的关系最近?)
    刚才你看到了如何回答第一类问题,下面来尝试回答第二类问题——谁是关系最近的芒果销售商。例如,朋友是一度关系,朋友的朋友是二度关系。

     在你看来,一度关系胜过二度关系,二度关系胜过三度关系,以此类推。因此,你应先在一度关系中搜索,确定其中没有芒果销售商后,才在二度关系中搜索。广度优先搜索就是这样做的!在广度优先搜索的执行过程中,搜索范围从起点开始逐渐向外延伸,即先检查一度关系,再检查二度关系。顺便问一句:将先检查Claire还是Anuj呢?Claire是一度关系,而Anuj是二度关系,因
    此将先检查Claire,后检查Anuj。

    你也可以这样看,一度关系在二度关系之前加入查找名单。

    你按顺序依次检查名单中的每个人,看看他是否是芒果销售商。这将先在一度关系中查找,再在二度关系中查找,因此找到的是关系最近的芒果销售商。广度优先搜索不仅查找从A到B的路径,而且找到的是最短的路径。

     注意,只有按添加顺序查找时,才能实现这样的目的。换句话说,如果Claire先于Anuj加入名单,就需要先检查Claire,再检查Anuj。如果Claire和Anuj都是芒果销售商,而你先检查Anuj再检查Claire,结果将如何呢?找到的芒果销售商并非是与你关系最近的,因为Anuj是你朋友的朋友,而Claire是你的朋友。因此,你需要按添加顺序进行检查。有一个可实现这种目的的数据
    结构,那就是队列(queue)。

    六、队列

    队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。

    队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。

    顺序队列

    建立顺序队列结构必须为其静态分配或动态申请一片连续的存储空间,并设置两个指针进行管理。一个是队头指针front,它指向队头元素;另一个是队尾指针rear,它指向下一个入队元素的存储位置,如图所示

    每次在队尾插入一个元素是,rear增1;每次在队头删除一个元素时,front增1。随着插入和删除操作的进行,队列元素的个数不断变化,队列所占的存储空间也在为队列结构所分配的连续空间中移动。当front=rear时,队列中没有任何元素,称为空队列。当rear增加到指向分配的连续空间之外时,队列无法再插入新元素,但这时往往还有大量可用空间未被占用,这些空间是已经出队的队列元素曾经占用过得存储单元。

    顺序队列中的溢出现象:

    (1) "下溢"现象:当队列为空时,做出队运算产生的溢出现象。“下溢”是正常现象,常用作程序控制转移的条件。

    (2)"真上溢"现象:当队列满时,做进栈运算产生空间溢出的现象。“真上溢”是一种出错状态,应设法避免。

    (3)"假上溢"现象:由于入队和出队操作中,头尾指针只增加不减小,致使被删元素的空间永远无法重新利用。当队列中实际的元素个数远远小于向量空间的规模时,也可能由于尾指针已超越向量空间的上界而不能做入队操作。该现象称为"假上溢"现象。

    循环队列

    在实际使用队列时,为了使队列空间能重复使用,往往对队列的使用方法稍加改进:无论插入或删除,一旦rear指针增1或front指针增1 时超出了所分配的队列空间,就让它指向这片连续空间的起始位置。自己真从MaxSize-1增1变到0,可用取余运算rear%MaxSize和front%MaxSize来实现。这实际上是把队列空间想象成一个环形空间,环形空间中的存储单元循环使用,用这种方法管理的队列也就称为循环队列。除了一些简单应用之外,真正实用的队列是循环队列。 [2] 

    在循环队列中,当队列为空时,有front=rear,而当所有队列空间全占满时,也有front=rear。为了区别这两种情况,规定循环队列最多只能有MaxSize-1个队列元素,当循环队列中只剩下一个空存储单元时,队列就已经满了。因此,队列判空的条件时front=rear,而队列判满的条件时front=(rear+1)%MaxSize。队空和队满的情况如图:

    七、广度优先搜索算法实现

    我们要从“你”出发找到“ANUJ”,关系表示为下图,使用广度优先搜索算法

     先概述一下这种算法的工作原理。

    但这样可能会出现一些问题,Peggy既是Alice的朋友又是Bob的朋友,因此她将被加入队列两次:一次是在添加Alice的朋友时,另一次是在添加Bob的朋友时。因此,搜索队列将包含两个Peggy。

    但你只需检查Peggy一次,看她是不是芒果销售商。如果你检查两次,就做了无用功。因此,检查完一个人后,应将其标记为已检查,且不再检查他。
    如果不这样做,就可能会导致无限循环。假设你的人际关系网类似于下面这样。

    一开始,搜索队列包含你的所有邻居。

    现在你检查Peggy。她不是芒果销售商,因此你将其所有邻居都加入搜索队列。

    接下来,你检查自己。你不是芒果销售商,因此你将你的所有邻居都加入搜索队列。

    以此类推。这将形成无限循环,因为搜索队列将在包含你和包含Peggy之间反复切换。

    检查一个人之前,要确认之前没检查过他,这很重要。为此,你可使用一个列表来记录检查过的人。

    首先,需要使用代码来实现图。图由多个节点组成。
    每个节点都与邻近节点相连,如果表示类似于“你→Bob”这样的关系呢?好在你知道的一种结构让你能够表示这种关系,它就是散列表!
    记住,散列表让你能够将键映射到值。在这里,你要将节点映射到其所有邻居。

    图不过是一系列的节点和边,因此在JAVA中,你可以使用HashMap来表示一个图。

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.concurrent.LinkedBlockingQueue;
    
    public class BFS {
    
    
        public static void main(String[] args) {
            HashMap<String,String[]> hashMap=new HashMap<>();
            hashMap.put("YOU",new String[]{"CLAIRE","ALICE","BOB"});
            hashMap.put("CLAIRE",new String[]{"YOU","JONNY","THON"});
            hashMap.put("JONNY",new String[]{"CLAIRE"});
            hashMap.put("THOH",new String[]{"CLAIRE"});
            hashMap.put("ALICE",new String[]{"YOU","PEGGY"});
            hashMap.put("BOB",new String[]{"YOU","PEGGY","ANUJ"});
            hashMap.put("PEGGY",new String[]{"BOB","ALICE"});
            hashMap.put("ANUJ",new String[]{"BOB"});
            Node target = findTarget("YOU","ANUJ",hashMap);
            //打印出最短路径的各个节点信息
            printSearPath(target);
        }
    
        /**
         * 打印出到达节点target所经过的各个节点信息
         * @param target
         */
        static void printSearPath(Node target) {
            if (target != null) {
                System.out.print("找到了目标节点:" + target.id + "\n");
    
                List<Node> searchPath = new ArrayList<Node>();
                searchPath.add(target);
                Node node = target.parent;
                while(node!=null) {
                    searchPath.add(node);
                    node = node.parent;
                }
                String path = "";
                for(int i=searchPath.size()-1;i>=0;i--) {
                    path += searchPath.get(i).id;
                    if(i!=0) {
                        path += "-->";
                    }
                }
                System.out.print("步数最短:"+path);
            } else {
                System.out.print("未找到了目标节点");
            }
        }
    
        static Node findTarget(String startId,String targetId,HashMap<String,String[]> map) {
            List<String> hasSearchList = new ArrayList<String>();
            LinkedBlockingQueue<Node> queue=new LinkedBlockingQueue<>();
            queue.offer(new Node(startId,null));
            while(!queue.isEmpty()) {
                Node node = queue.poll();
                if(hasSearchList.contains(node.id)) {
                    continue;
                }
                System.out.print("判断节点:" + node.id +"\n");
                if (targetId.equals(node.id)) {
                    return node;
                }
                hasSearchList.add(node.id);
                if (map.get(node.id) != null && map.get(node.id).length > 0) {
                    for (String childId : map.get(node.id)) {
                        queue.offer(new Node(childId,node));
                    }
                }
            }
            return null;
        }
    
        static class Node{
            public String id;
            public Node parent;
            public Node(String id,Node parent) {
                this.id = id;
                this.parent = parent;
            }
        }
    }

    运行时间

    如果你在你的整个人际关系网中搜索芒果销售商,就意味着你将沿每条边前行(记住,边是从一个人到另一个人的箭头或连接),因此运行时间至少为O(边数)。
    你还使用了一个队列,其中包含要检查的每个人。将一个人添加到队列需要的时间是固定的,即为O(1),因此对每个人都这样做需要的总时间为O(人数)。所以,广度优先搜索的运行时间为O(人数 + 边数),这通常写作O(V + E),其中V为顶点(vertice)数,E为边数。

    展开全文
  • BFS/DFS算法解析【算法入门]2018/6/21.前言和树的遍历类似,图的遍历也是从图中某点出发,...根据搜索路径的不同,我们可以将遍历图的方法分为两种:广度优先搜索和深度优先搜索。2.图的基本概念2.1.无向图和无向...

    BFS和DFS算法解析

    【算法入门】

    2018/6/2

    1.前言

    和树的遍历类似,图的遍历也是从图中某点出发,然后按照某种方法对图中所有顶点进行访问,且仅访问一次。

    但是图的遍历相对树而言要更为复杂。因为图中的任意顶点都可能与其他顶点相邻,所以在图的遍历中必须记录已被访问的顶点,避免重复访问。

    根据搜索路径的不同,我们可以将遍历图的方法分为两种:广度优先搜索和深度优先搜索。

    2.图的基本概念


    2.1.无向图和无向图

    顶点对(u,v)是无序的,即(u,v)和(v,u)是同一条边。常用一对圆括号表示。


    图2-1-1 无向图示例

    顶点对<u,v>是有序的,它是指从顶点u到顶点 v的一条有向边。其中u是有向边的始点,v是有向边的终点。常用一对尖括号表示。

    图2-1-2 有向图示例

    2.2.权和网

    图的每条边上可能存在具有某种含义的数值,称该数值为该边上的权。而这种带权的图被称为网。

    2.3.连通图与非连通图

    连通图:在无向图G中,从顶点v到顶点v'有路径,则称v和v'是联通的。若图中任意两顶点v、v'∈V,v和v'之间均联通,则称G是连通图。上述两图均为连通图。

    非连通图:若无向图G中,存在v和v'之间不连通,则称G是非连通图。


    图2-3 非连通图示例

    3.广度优先搜索


    3.1.算法的基本思路

    广度优先搜索类似于树的层次遍历过程。它需要借助一个队列来实现。如图2-1-1所示,要想遍历从v0到v6的每一个顶点,我们可以设v0为第一层,v1、v2、v3为第二层,v4、v5为第三层,v6为第四层,再逐个遍历每一层的每个顶点。

    具体过程如下:

      1.准备工作:创建一个visited数组,用来记录已被访问过的顶点;创建一个队列,用来存放每一层的顶点;初始化图G。

      2.从图中的v0开始访问,将的visited[v0]数组的值设置为true,同时将v0入队。

    3.只要队列不空,则重复如下操作:

        (1)队头顶点u出队。

        (2)依次检查u的所有邻接顶点w,若visited[w]的值为false,则访问w,并将visited[w]置为true,同时将w入队。

    3.2.算法的实现过程

    白色表示未被访问,灰色表示即将访问,黑色表示已访问。

    visited数组:0表示未访问,1表示以访问。

    队列:队头出元素,队尾进元素。

    1.初始时全部顶点均未被访问,visited数组初始化为0,队列中没有元素。



    图3-2-1

    2.即将访问顶点v0。


    图3-2-2


    3.访问顶点v0,并置visited[0]的值为1,同时将v0入队。


    图3-2-3

    4.将v0出队,访问v0的邻接点v2。判断visited[2],因为visited[2]的值为0,访问v2。


    图3-2-4

    5.将visited[2]置为1,并将v2入队。


    图3-2-5

    6.访问v0邻接点v1。判断visited[1],因为visited[1]的值为0,访问v1。


    图3-2-6

    7.将visited[1]置为0,并将v1入队。


    图3-2-7

    8.判断visited[3],因为它的值为0,访问v3。将visited[3]置为0,并将v3入队。


    图3-2-8

    9.v0的全部邻接点均已被访问完毕。将队头元素v2出队,开始访问v2的所有邻接点。

    开始访问v2邻接点v0,判断visited[0],因为其值为1,不进行访问。

    继续访问v2邻接点v4,判断visited[4],因为其值为0,访问v4,如下图:


    图3-2-9

    10.将visited[4]置为1,并将v4入队。


    图3-2-10

    11.v2的全部邻接点均已被访问完毕。将队头元素v1出队,开始访问v1的所有邻接点。

    开始访问v1邻接点v0,因为visited[0]值为1,不进行访问。

    继续访问v1邻接点v4,因为visited[4]的值为1,不进行访问。

    继续访问v1邻接点v5,因为visited[5]值为0,访问v5,如下图:


    图3-2-11

    12.将visited[5]置为1,并将v5入队。

    图3-2-12

    13.v1的全部邻接点均已被访问完毕,将队头元素v3出队,开始访问v3的所有邻接点。

    开始访问v3邻接点v0,因为visited[0]值为1,不进行访问。

    继续访问v3邻接点v5,因为visited[5]值为1,不进行访问。


    图3-2-13

    14.v3的全部邻接点均已被访问完毕,将队头元素v4出队,开始访问v4的所有邻接点。

    开始访问v4的邻接点v2,因为visited[2]的值为1,不进行访问。

    继续访问v4的邻接点v6,因为visited[6]的值为0,访问v6,如下图:


    图3-2-14

    15.将visited[6]值为1,并将v6入队。


    图3-2-15

    16.v4的全部邻接点均已被访问完毕,将队头元素v5出队,开始访问v5的所有邻接点。

    开始访问v5邻接点v3,因为visited[3]的值为1,不进行访问。

    继续访问v5邻接点v6,因为visited[6]的值为1,不进行访问。


    图3-2-16

    17.v5的全部邻接点均已被访问完毕,将队头元素v6出队,开始访问v6的所有邻接点。

    开始访问v6邻接点v4,因为visited[4]的值为1,不进行访问。

    继续访问v6邻接点v5,因为visited[5]的值文1,不进行访问。


    图3-2-17

    18.队列为空,退出循环,全部顶点均访问完毕。


    图3-2-18
    3.3具体代码的实现
    3.3.1用邻接矩阵表示图的广度优先搜索
    /*一些量的定义*/
    queue<char> q;				//定义一个队列,使用库函数queue
    #define MVNum 100			//表示最大顶点个数
    bool visited[MVNum];		        //定义一个visited数组,记录已被访问的顶点
    /*邻接矩阵存储表示*/
    typedef struct AMGraph
    {
    	char vexs[MVNum];            //顶点表
    	int arcs[MVNum][MVNum];      //邻接矩阵
    	int vexnum, arcnum;          //当前的顶点数和边数
    }AMGraph;
    /*找到顶点v的对应下标*/
    int LocateVex(AMGraph &G, char v)
    {
    	int i;
    	for (i = 0; i < G.vexnum; i++)
    		if (G.vexs[i] == v)
    			return i;
    }
    /*采用邻接矩阵表示法,创建无向图G*/
    int CreateUDG_1(AMGraph &G)
    {
    	int i, j, k;
    	char v1, v2;
    	scanf("%d%d", &G.vexnum, &G.arcnum);	                //输入总顶点数,总边数
    	getchar();				   	        //获取'\n’,防止其对之后的字符输入造成影响
    	for (i = 0; i < G.vexnum; i++)			
    		scanf("%c", &G.vexs[i]);			//依次输入点的信息
    	for (i = 0; i < G.vexnum; i++)
    		for (j = 0; j < G.vexnum; j++)
    			G.arcs[i][j] = 0;			//初始化邻接矩阵边,0表示顶点i和j之间无边
    	for (k = 0; k < G.arcnum; k++)
    	{
    		getchar();
    		scanf("%c%c", &v1, &v2);			//输入一条边依附的顶点
    		i = LocateVex(G, v1);				//找到顶点i的下标
    		j = LocateVex(G, v2);				//找到顶点j的下标
    		G.arcs[i][j] = G.arcs[j][i] = 1;	        //1表示顶点i和j之间有边,无向图不区分方向
    	}
    	return 1;
    }
    /*采用邻接矩阵表示图的广度优先遍历*/
    void BFS_AM(AMGraph &G,char v0)
    {
    /*从v0元素开始访问图*/
    
    	int u,i,v,w;
    	v = LocateVex(G,v0);                            //找到v0对应的下标
    	printf("%c ", v0);                              //打印v0
    	visited[v] = 1;		                        //顶点v0已被访问
    	q.push(v0);			                //将v0入队
    
    	while (!q.empty())
    	{
    		u = q.front();				//将队头元素u出队,开始访问u的所有邻接点
    		v = LocateVex(G, u);			//得到顶点u的对应下标
    		q.pop();				//将顶点u出队
    		for (i = 0; i < G.vexnum; i++)
    		{
    			w = G.vexs[i];
    			if (G.arcs[v][i] && !visited[i])//顶点u和w间有边,且顶点w未被访问
    			{
    				printf("%c ", w);	//打印顶点w
    				q.push(w);		//将顶点w入队
    				visited[i] = 1;		//顶点w已被访问
    			}
    		}
    	}
    }
    
    3.3.2用邻接表表示图的广度优先搜索
    /*找到顶点对应的下标*/
    int LocateVex(ALGraph &G, char v)
    {
    	int i;
    	for (i = 0; i < G.vexnum; i++)
    		if (v == G.vertices[i].data)
    			return i;
    }
    /*邻接表存储表示*/
    typedef struct ArcNode	        //边结点
    {
    	int adjvex;		//该边所指向的顶点的位置
    	ArcNode *nextarc;	//指向下一条边的指针
    	int info;		//和边相关的信息,如权值
    }ArcNode;
    
    typedef struct VexNode		//表头结点
    {
    	char data;				
    	ArcNode *firstarc;	//指向第一条依附该顶点的边的指针
    }VexNode,AdjList[MVNum];	//AbjList表示一个表头结点表
    
    typedef struct ALGraph
    {
    	AdjList vertices;
    	int vexnum, arcnum;
    }ALGraph;
    /*采用邻接表表示法,创建无向图G*/
    int CreateUDG_2(ALGraph &G)
    {
    	int i, j, k;
    	char v1, v2;
    	scanf("%d%d", &G.vexnum, &G.arcnum);	        //输入总顶点数,总边数
    	getchar();
    	for (i = 0; i < G.vexnum; i++)			//输入各顶点,构造表头结点表
    	{
    		scanf("%c", &G.vertices[i].data);	//输入顶点值
    		G.vertices[i].firstarc = NULL;		//初始化每个表头结点的指针域为NULL
    	}
    	for (k = 0; k < G.arcnum; k++)			//输入各边,构造邻接表
    	{
    		getchar();
    		scanf("%c%c", &v1, &v2);			//输入一条边依附的两个顶点
    		i = LocateVex(G, v1);				//找到顶点i的下标
    		j = LocateVex(G, v2);				//找到顶点j的下标
    		ArcNode *p1 = new ArcNode;			//创建一个边结点*p1
    		p1->adjvex = j;						//其邻接点域为j
    		p1->nextarc = G.vertices[i].firstarc; G.vertices[i].firstarc = p1; // 将新结点*p插入到顶点v1的边表头部
    		ArcNode *p2 = new ArcNode;			//生成另一个对称的新的表结点*p2
    		p2->adjvex = i;
    		p2->nextarc = G.vertices[j].firstarc;
    		G.vertices[j].firstarc = p1;
    	}
    	return 1;
    }
    /*采用邻接表表示图的广度优先遍历*/
    void BFS_AL(ALGraph &G, char v0)
    {
    	int u,w,v;
    	ArcNode *p;
    	printf("%c ", v0);		                                        //打印顶点v0
    	v = LocateVex(G, v0);	                                                //找到v0对应的下标
    	visited[v] = 1;			                                        //顶点v0已被访问
    	q.push(v0);				                                //将顶点v0入队
    	while (!q.empty())
    	{
    		u = q.front();		                                        //将顶点元素u出队,开始访问u的所有邻接点
    		v = LocateVex(G, u);                                            //得到顶点u的对应下标
    		q.pop();			//将顶点u出队
    		for (p = G.vertices[v].firstarc; p; p = p->nextarc)		//遍历顶点u的邻接点
    		{
    			w = p->adjvex;	
    			if (!visited[w])	//顶点p未被访问
    			{
    				printf("%c ", G.vertices[w].data);	        //打印顶点p
    				visited[w] = 1;				        //顶点p已被访问
    				q.push(G.vertices[w].data);			//将顶点p入队
    			}
    		}
    	}
    }
    3.4.非联通图的广度优先遍历的实现方法
    /*广度优先搜索非连通图*/
    void BFSTraverse(AMGraph G)
    {
    	int v;
    	for (v = 0; v < G.vexnum; v++)
    		visited[v] = 0;							//将visited数组初始化
    	for (v = 0; v < G.vexnum; v++)
    		if (!visited[v]) BFS_AM(G, G.vexs[v]);	                        //对尚未访问的顶点调用BFS
    }


    4.深度优先搜索


    4.1算法的基本思路

    深度优先搜索类似于树的先序遍历,具体过程如下:


    准备工作:创建一个visited数组,用于记录所有被访问过的顶点。


    1.从图中v0出发,访问v0。

    2.找出v0的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点,重复此步骤,直至刚访问过的顶点没有未被访问的邻接点为止。

    3.返回前一个访问过的仍有未被访问邻接点的顶点,继续访问该顶点的下一个未被访问领接点。

    4.重复2,3步骤,直至所有顶点均被访问,搜索结束。


    4.2算法的实现过程

    1.初始时所有顶点均未被访问,visited数组为空。




    图4-2-1

    2.即将访问v0。


    图4-2-2

    3.访问v0,并将visited[0]的值置为1。


    图4-2-3

    4.访问v0的邻接点v2,判断visited[2],因其值为0,访问v2。


    图4-2-4

    5.将visited[2]置为1。


    图4-2-5

    6.访问v2的邻接点v0,判断visited[0],其值为1,不访问。

    继续访问v2的邻接点v4,判断visited[4],其值为0,访问v4。


    图4-2-6

    7.将visited[4]置为1。


    图4-2-7

    8.访问v4的邻接点v1,判断visited[1],其值为0,访问v1。



    图4-2-8

    9.将visited[1]置为1。


    图4-2-9

    10.访问v1的邻接点v0,判断visited[0],其值为1,不访问。

    继续访问v1的邻接点v4,判断visited[4],其值为1,不访问。

    继续访问v1的邻接点v5,判读visited[5],其值为0,访问v5。


    图4-2-10

    11.将visited[5]置为1。


    图4-2-11

    12.访问v5的邻接点v1,判断visited[1],其值为1,不访问。

    继续访问v5的邻接点v3,判断visited[3],其值为0,访问v3。


    图4-2-12

    13.将visited[1]置为1。


    图4-2-13

    14.访问v3的邻接点v0,判断visited[0],其值为1,不访问。

    继续访问v3的邻接点v5,判断visited[5],其值为1,不访问。

    v3所有邻接点均已被访问,回溯到其上一个顶点v5,遍历v5所有邻接点。

    访问v5的邻接点v6,判断visited[6],其值为0,访问v6。


    图4-2-14

    15.将visited[6]置为1。


    图4-2-15

    16.访问v6的邻接点v4,判断visited[4],其值为1,不访问。

    访问v6的邻接点v5,判断visited[5],其值为1,不访问。

    v6所有邻接点均已被访问,回溯到其上一个顶点v5,遍历v5剩余邻接点。


    图4-2-16

    17.v5所有邻接点均已被访问,回溯到其上一个顶点v1。

    v1所有邻接点均已被访问,回溯到其上一个顶点v4,遍历v4剩余邻接点v6。

    v4所有邻接点均已被访问,回溯到其上一个顶点v2。

    v2所有邻接点均已被访问,回溯到其上一个顶点v1,遍历v1剩余邻接点v3。

    v1所有邻接点均已被访问,搜索结束。


    图4-2-17

    4.3具体代码实现

    4.3.1用邻接矩阵表示图的深度优先搜索

    邻接矩阵的创建在上述已描述过,这里不再赘述

    void DFS_AM(AMGraph &G, int v)
    {
    	int w;
    	printf("%c ", G.vexs[v]);
    	visited[v] = 1;
    	for (w = 0; w < G.vexnum; w++)
    		if (G.arcs[v][w]&&!visited[w]) //递归调用
    			DFS_AM(G,w);
    }
    4.3.2用邻接表表示图的深度优先搜素

    邻接表的创建在上述已描述过,这里不再赘述。

    void DFS_AL(ALGraph &G, int v)
    {
    	int w;
    	printf("%c ", G.vertices[v].data);
    	visited[v] = 1;
    	ArcNode *p = new ArcNode;
    	p = G.vertices[v].firstarc;
    	while (p)
    	{
    		w = p->adjvex;
    		if (!visited[w]) DFS_AL(G, w);
    		p = p->nextarc;
    	}
    }


    展开全文
  • 树的广度优先插入和广度优先遍历 树的广度优先插入首先写出泛型(java)定义树类然后就是我们的广度优先插入树的广度优先遍历 ) 树的广度优先插入 首先写出泛型(java) package com.bdrjxy.web; public class ...


    )

    树的广度优先插入

    首先写出泛型(java)

    package com.bdrjxy.web;
    
    public class QueueTest< T > {
    private T[] data = (T[]) new Object[20];
    private int start = 0;
    private int end = 0;
    
    public void push(T newint) {
    	if( end - start == data.length ) {
    		T[] datanew = (T[]) new Object[ data.length * 2 ];
    		for(int i = 0; i < data.length; i++) {
    			datanew[i] = data[  ( start + i ) % data.length ];
    		}
    		start = 0;
    		end = start + data.length;
    		data = datanew;
    	}
    	data[ end % data.length ] = newint;
    	end++;
    }
    public T get() {
    	if( end == start ) {
    		return null;
    	}
    	T result = data[start % data.length];
    	start++;
    	return result;
    }
    }
    

    定义树类

    package com.bdrjxy.web;
    
    public class Tree {
    
    public int value;//要事先定义这些值
    
    public Tree left;
    
    public Tree right;
    @Override
    public String toString() {
    	return "Tree [value=" + value + ", left=" + left + ", right=" + right + "]";
    }
    }
    

    关于
    从队列中取出的每一个结点,当它完成自己的使命之后(指向新的结点,也就是它的左孩子和右孩子),就会被自动回收

    然后就是我们的广度优先插入

    package com.bdrjxy.web;
    
    public class Test {
    
    public static void main(String[] args) {
    	// TODO Auto-generated method stub
    	int[] arr = {1,4,7,2,43,12,66,43,21,87,67,54,32,11,0};
    	
    	Tree root = new Tree();//定义树的根结点
    	root.value = arr[0];//赋值
    	
    	QueueTest<Tree> queue = new QueueTest<Tree>();//新建队列
    	queue.push(root);//将根结点放入队列
    	for(int i = 1; i < arr.length;) {
    		Tree newtree = queue.get();//最先放进去最先拿出来,队列的特性
    		Tree nodel = new Tree();//每拿出来一个,让它分别指向新的结点之后,拿出来的这个被回收,然后继续先入先出
    		nodel.value = arr[ i ];
    		newtree.left = nodel;
    		queue.push(nodel);
    		if(i+1<arr.length){
    		Tree noder = new Tree();
    		noder.value = arr[ i+1 ];
    		newtree.right = noder;
    		queue.push(noder);
    	}
    	else{
    		break;
    	}					
    		i += 2;//每次都有两个新的结点,所以循环每次加2
    		
    	}
    	System.out.println(root);
    }
    
    }
    

    树的广度优先遍历

    在这里插入图片描述

    展开全文
  • 搜索是常见的解决问题的方法,常见的搜索方式有深度优先搜搜和广度优先搜索,一般来说都是搜索只不过方式略有不同,所以我之前在学习搜索的时候一直都是练习的深度优先搜索,也很少有哪些问题就必须用深度优先或者...
  • 广度优先搜索 matlab

    2018-10-09 16:29:28
    广度优先搜索(也称宽度优先搜索,缩写BFS,以下采用广度来描述)是连通图的一种遍历策略。本程序用Matlab语言实现广度优先算法
  • 深度优先遍历(DFS)和广度优先遍历(BFS)

    万次阅读 多人点赞 2019-05-09 10:46:23
    图的搜索有两种方式,一种是深度优先搜索(Depth-First-Search),另一种是广度优先搜索(Breadth-First-Search),接下来,我们来写一下这些搜索方式的Java实现,同样的,这里的代码均继承了自定义的EnhanceModual...
  • 广度优先BFS.cpp

    2021-04-22 08:24:56
    广度优先算法C/C++实现
  • 本章的第一节中,介绍了有关...同理,广度优先生成树,图 1 无向图以顶点 V1 为起始点进行广度优先搜索遍历得到的树,如图 3 所示:图 3 广度优先生成树非非连通图在进行遍历时,实则是对非连通图中每个连通分量分别...
  • 图的广度优先搜索(BFS)算法:基本概念:对于给定的图G=(V,E),广度优先搜索从一个源顶点出发,通过对其邻接表进行遍历(搜索),可以发现所有与源顶点邻接的顶点。依次类推,不断地向外扩展,进而发现与源顶点的邻接点...
  • 广度优先搜索

    2013-04-27 08:20:29
    广度优先搜索
  • 深度优先搜索与广度优先搜索深度优先搜索广度优先搜索 深度优先搜索 广度优先搜索
  • 本文将讨论图遍历搜索算法背后的逻辑,并且通过简单的实例来理解广度优先搜索算法的工作原理。 图遍历搜索算法简介 按照最浅显的解释,访问(visiting)和探索(exploring)图并且进行处理的过程就称为“图遍历”,...
  • 深度优先和广度优先

    2018-12-22 17:38:00
    深度优先(广度优先的是一层数为优先): 深度优先和广度优先的实现:深度是递归,广度是队列 深度优先过程的代码实现: 广度优先过程的代码实现: 转载于:...
  • 广度优先搜索

    2019-06-20 08:30:18
    人工智能技术导论课程的广度优先搜索,代码完整,内部有open和close表显示
  • 邻接矩阵实现图,Java实现数据结构,Java实现图的深度优先遍历,广度优先遍历
  • 实现深度优先广度优先算法
  • 广度优先算法

    2011-12-20 05:37:20
    源代码广度优先算法实例,有助于对广度优先搜索的了解
  • 何为广度优先遍历呢?广度优先遍历(BFS),又叫宽度优先搜索或横向优先搜索,是从根结点开始沿着树的宽度搜索遍历,将离根节点最近的节点先遍历出来,在继续深挖下去。基本思想是:1、从图中某个顶点V0出发,并访问...
  • 深度优先广度优先

    2017-08-30 13:32:00
    深度优先:纵向 广度优先:横向                     
  • 本程序实现了对一颗树的广度优先搜索,通过本程序还可以判断图的连通性 本程序实现了对一颗树的广度优先搜索,通过本程序还可以判断图的连通性
  • 这里不使用真实的地铁数据,而采用简单的随机生成的数据来进行模拟station 绘制一下生成随机地铁路线G 生成图信息,即无向图的连接信息(每个节点连接的节点是什么)graph_info 标准的广度优先和深度优先遍历算法...
  • 主要介绍了深度优先与广度优先Java实现代码示例,具有一定借鉴价值,需要的朋友可以参考下。
  • 今天我们要学习的是另外一种常用的搜索算法——广度优先遍历,它并没有固定的代码格式,只是一种遍历方式的思想。广度优先遍历一般用于求最短路径问题,我们用一个社交图来举例。上图是一个无向图,用0表示两个人...
  • 主要介绍了python图的深度优先和广度优先算法,结合实例形式分析了图的深度优先算法与广度优先算法相关概念、原理、实现技巧与操作注意事项,需要的朋友可以参考下
  • 广度优先遍历 实例

    2016-08-02 15:17:20
    广度优先遍历作为一个初学者必备的技能,此资源免费,广度优先遍历是连通图的一种遍历策略。因为它的思想是从一个顶点V0开始,辐射状地优先遍历其周围较广的区域,故得名
  • 本文主要叙述了图的遍历算法中的广度优先搜索(Breadth-First-Search)算法,是非常经典的算法,可供C++程序员参考借鉴之用。具体如下: 首先,图的遍历是指从图中的某一个顶点出发,按照某种搜索方法沿着图中的边...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,849
精华内容 9,139
关键字:

广度优先