深度优先遍历 订阅
深度优先搜索是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) 。在一个HTML文件中,当一个超链被选择后,被链接的HTML文件将执行深度优先搜索,即在搜索其余的超链结果之前必须先完整地搜索单独的一条链。深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,说明搜索已经结束。 [1] 展开全文
深度优先搜索是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) 。在一个HTML文件中,当一个超链被选择后,被链接的HTML文件将执行深度优先搜索,即在搜索其余的超链结果之前必须先完整地搜索单独的一条链。深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,说明搜索已经结束。 [1]
信息
提出者
霍普克洛夫特与罗伯特·塔扬
应用学科
计算机
中文名
深度优先搜索
外文名
Depth-First-Search
深度优先搜索详细解释
事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.举例说明之:下图是一个无向图,如果我们从A点发起深度优先搜索(以下的访问次序并不是唯一的,第二个点既可以是B也可以是C,D),则我们可能得到如下的一个访问过程:A->B->E(没有路了!回溯到A)->C->F->H->G->D(没有路,最终回溯到A,A也没有未访问的相邻节点,本次搜索结束). 简要说明深度优先搜索的特点:每次深度优先搜索的结果必然是图的一个连通分量.深度优先搜索可以从多点发起.如果将每个节点在深度优先搜索过程中的"结束时间"排序(具体做法是创建一个list,然后在每个节点的相邻节点都已被访问的情况下,将该节点加入list结尾,然后逆转整个链表),则我们可以得到所谓的"拓扑排序",即topological sort. [1] 
收起全文
精华内容
下载资源
问答
  • 深度优先遍历
    2022-03-04 13:56:54

    之前关于深度和广度优先遍历觉得算是比较简单的东西了,特别是深度优先遍历,用递归实现起来几乎是非常自然的,然而最近进行了一些思考探索,仍然有一些非常有意思的点,不论是从实际应用,还是优化方向。由于线性结构遍历比较朴素就不讨论了,这里主要针对图和树两种模型来探讨。

    深度优先遍历还是广度优先遍历

    从结果上看,二者都是遍历整个关联结构,而且时间复杂度都一样,跟对象群的规模呈线性关系, 没有太大的影响,但过程上还是有些差别。

    我们先来聊下深度优先遍历

    深度优先遍历有递归和非递归的写法,对于递归来说,它的代价在于递归的栈开销,所来带的问题是当递归的层次过深,便可能有栈溢出的问题。我们知道栈空间相比堆空间来说要小很多。但递归的好处在于设计思路清晰,实现起来非常的自然和容易。因此在具有较多栈空间开销的递归实现中(参数多,局部变量多),就要考虑优化。

    我们可以看一个经典的例子:C#中的Sort

    我们一般认为商业代码中,会使用快排作为默认的排序实现,实际上C#中的Sort有两种以快排为原型的改进,一种称为DepthLimitedQuickSort,一种称为IntroSort

    通过DepthLimited关键字我们也能猜出大概的意思,就是对递归深度做限制,我们看一段源代码

    internal static void DepthLimitedQuickSort(
          T[] keys,
          int left,
          int right,
          IComparer<T> comparer,
          int depthLimit)
        {
          .....
          while (depthLimit != 0)
          {
            --depthLimit;
            if (index2 - left <= right - index1)
            {
              if (left < index2)
                ArraySortHelper<T>.DepthLimitedQuickSort(keys, left, index2, comparer, depthLimit);
              left = index1;
            }
            else
            {
              if (index1 < right)
                ArraySortHelper<T>.DepthLimitedQuickSort(keys, index1, right, comparer, depthLimit);
              right = index2;
            }
            if (left >= right)
              return;
          }
          ArraySortHelper<T>.Heapsort(keys, left, right, comparer);
        }

    期间我省略了一些不重要的代码,通过观察可以知道,在递归深度限制之内,仍然是递归调用排序,当超出递归深度后转而调用堆排序,这就是对递归深度的优化。

    而C#官方给的这个最大递归深度是32

    ArraySortHelper<T>.DepthLimitedQuickSort(keys, index, length + index - 1, comparer, 32);

    这里给出微软官方底层实现的源代码链接,感兴趣的可以看一下,多看源码收获还是很大的,读商业代码和学习代码完全是两种感受。

    https://referencesource.microsoft.com/#mscorlib/system/collections/generic/arraysorthelper.cs

    至于IntroSort就做了更多的优化,不仅有深度限制,还会在元素个数少于16个的时候,直接调用插入排序等其他针对更小规模数据的排序方法。

    有些同学可能会觉得,32是不是很大了,按照我们对二叉树的理解,一棵32层深的书,包含的叶子节点数在2^31。这只是理论上限,虽然快排做了很多优化,但快排的轴划分毕竟不会是完全均衡的。我们学习快排的时候知道,快排在最差的情况下,复杂度是O(N^2)的,就是因为轴划分不均等的影响。在此种情况下,递归深度就不能按照这个来计算了。

    那如果是非递归实现呢?我们知道深度优先遍历的非递归实现依赖于一个栈,递归过程中会将遍历的节点子孩子压入栈,通过逆序弹出来达到深度有限的效果,但对于一个简单实现来说,栈最大存储占用并不是仅仅和树的高度有关,因为你总是会将遍历的某个子树的第一个节点的所有孩子都压入栈。在极端情况下,当你真正开始遍历第一个节点的时候。所有的元素都已经压入栈中,感兴趣的同学可以思考下这是种什么情况。

    另外在一些通过深度优先遍历进行重新分配空间的情况来说,比如GC中的压缩算法,通过深度优先遍历,可以让有关联的对象排列在一起,从而增加缓存的命中,提高速度。

    另外请注意,前边所涉及的情况都是在假设我们所遍历的结构是一棵树,如果是图结构的话,还必须考虑带环的问题,需要记录下已经访问过点以防止重复访问。

    我们在笔试的时候曾出过一道题目,就是深拷贝一个对象。很多同学能够意识到对引用类型对象需要递归的进行拷贝,却忽略了不同属性引用同一个对象,属性引用自身,以及环式引用(循环链表)。在考虑遍历时一定要对所遍历对象的模型认识清楚,从而选择最优的实现方法。

    接下来我们说一下广度优先遍历

    广度优先遍历一般是需要依赖队列这种数据结构来实现的,其天然的迭代属性使得其结构性开销通常来说会比较小。如果我们把遍历的过程展开成一棵遍历树的情况下会发现,队列中最大元素的个数是和树的宽度相关的,而深度优先遍历之前也说了,是和树的高度相关的。因此树越扁平化队列的峰值消耗就会越大。但在实际应用中,广度优先遍历的消耗会比预计的要大。我们从两点来考虑这个问题。

    第一、从缓存命中的角度看,队列的入队和出队是在队列的两头进行操作的,相对于总是从一端进行操作的栈来说,当队列元素过多的时候,缓存失效的可能性会更大。

    第二、从容器的扩容机制来说,队列底层也是用数组实现的(C#),当数组元素不足以容纳元素的个数的时候,数组会以2倍扩容的机制进行扩容。当我们卡到临界点的时候,甚至可能会造成一倍的空间浪费。

    关于空间的浪费其实是增大了峰值内存的消耗,我们关注峰值是因为过高的峰值内存可能会引起程序的闪退,特别是做移动应用开发的时候。因为这部分内存最终在遍历结束后会清空,所以并不会造成后续的困扰。

    在一些特定的情况下,广度优先遍历可以省去队列这种额外的需要。例如我们需要用线性表收集所有遍历后的结果。就可以直接将线性表本身作为队列,通过双指针的模式来模拟队列的行为,就可以达到最终的效果。

    举个例子:

    List<Node> res = new List<Node>();
    res.Add(root);
    for (int i = 0; i < res.Count; ++i)
    {
        res[i].Children.ForEach(child => res.Add(child));
    }

    请注意,这里仍然是以C#为例去写的,请确保你足够了解系统的机制,比如这里的res.Count,这是一个属性而不是表达式,并不会提前计算结果。因此才能保证每次插入后循环会继续进行。

    通过IL代码可以看到,每次循环都会重新调用方法获取Count

    IL_0048: callvirt     instance int32 class [mscorlib]System.Collections.Generic.List`1<int32>::get_Count()

    关于非递归算法的补充说明

    非递归的实现方式还有一个好处是,可以做分步处理,因为所有的待考察对象都维护在一个栈或队列等容器里,因此我们可以将这个容器保存起来,每次只执行规定的迭代次数。这也是了解垃圾回收算法中的增量式回收的概念时看到的。不过递归算法,特别是广度优先,改写为多线程似乎更容易一些,因为没有实际应用过,就不讨论了

    总结

    在实际的应用开发中,搜索和遍历是经常遇到的情况,这里仅针对两种常见的遍历方法进行了一些深入的探讨,不过也可以发现,我们例子里多数情况下,遍历的过程并不会改变原对象,而在实际的处理中,还会有在遍历中操作等更复杂的行为,比如先序遍历和后续遍历,对于遍历过程中比较依赖于父子关系的情况,就要仔细考虑实现的模式了。代码设计的难易度,时间,空间效率都要根据实际情况去做权衡。

    更多相关内容
  • 主要介绍了JavaScript树的深度优先遍历和广度优先遍历算法,结合实例形式分析了JavaScript树的深度优先遍历、广度优先遍历递归与非递归相关实现技巧,需要的朋友可以参考下
  • 使用邻接表表示法创建无向图,然后使用非递归算法进行深度优先遍历和广度优先遍历
  • 主要介绍了python实现树的深度优先遍历与广度优先遍历,详细分析了树的深度优先遍历与广度优先遍历原理及Python相关实现技巧,需要的朋友可以参考下
  • 主要介绍了Java实现二叉树的深度优先遍历和广度优先遍历算法,结合实例形式详细分析了二叉树的定义、深度优先遍历与广度优先遍历算法原理与相关操作实现技巧,需要的朋友可以参考下
  • 深度优先遍历的实现; 广度优先遍历的实现;
  • 天津理工大学实验报告 学院系名称 计算机与通信工程学院 姓名 学号 专业 计算机科学与技术 班级 2009 级 1 班 实验项目 实验四 图的深度优先与广度优先遍历 课程名称 数据结构与算法 课程代码 实验时间 2011 年 5 月...
  • C++实现,数据结构,图的邻接矩阵表示,深度优先遍历,广度优先遍历,DFS,BFS,为什么要五十个字才能上传啊
  • 深度优先遍历DFS 与树的先序遍历比较类似。 假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的...
  • 深度优先遍历和广度优先遍历

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

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

    展开全文
  • 建立图的邻接矩阵或邻接表存储并在此基础上实现图的深度优先遍历和广度优先遍历.doc
  • NULL 博文链接:https://128kj.iteye.com/blog/1734260
  • c++实现图的邻接表深度优先遍历,广度优先遍历
  • c数据结构链表数组以及深度优先遍历,假设给定图G的初态是所有顶点均未曾访问过。在G中任选一顶点v为初始出发点(源点),则深度优先遍历可定义如下:首先访问出发点v,并将其标记为已访问过;然后依次从v出发搜索v的...
  • 本文实例讲述了python实现树的深度优先遍历与广度优先遍历。分享给大家供大家参考,具体如下: 广度优先(层次遍历) 从树的root开始,从上到下从左到右遍历整个树的节点 数和二叉树的区别就是,二叉树只有左右两个...
  • 图的遍历 广度 深度 c语言

    一、深度优先遍历

    1、简介

    深度优先遍历是指按照深度方向搜索,它类似于树的先根遍历,是树的先根遍历的推广。

    基本思想(通俗)

    选一条路走到 ,直到 走不通,就 原路返回看看 是否还有路可走,如果返回到起点还无路可走,说明深度优先遍历已完成。

    2、举例说明

    这是要深度遍历的无向图

     

     深度遍历依次访问的为:

    v1->v2->v4->v8->v5->v3->v6->v7

    3、C语言代码 

    (1)邻接矩阵存储无向图。

    12345678
    101100000
    210011000
    310000110
    401000001
    501000001
    600100000
    700100000
    800011000

    对于图的存储,请参考我的文章:图的三种存储结构:邻接矩阵表示法+链表法+十字链表法

    存储无向图

    #include<stdio.h>
    #define MAX_VERTEM_NUM 10 
    #define INFINITY 32768
     
    typedef enum{
    	DG,DN,UDG,UDN	
    }graghKind;
    //digraph  DG有向图
    //directed network  DN有向网
    //undirected graph  UDG无向图
    //undirected network  UDN无向网
     
    typedef int vertemData;
     
    typedef struct {
    	vertemData vert[MAX_VERTEM_NUM];			//顶点向量
    	int adj[MAX_VERTEM_NUM][MAX_VERTEM_NUM];	//邻接矩阵
    	int vertNum,arcNum;							//图的顶点数和弧数
    	graghKind gragh;							//图的类型
    }adjMatrix;
     
    //求顶点位置
    int locateVertem(adjMatrix *G,vertemData v){
    	for(int j=0;j<G->vertNum;j++)
    	{
    		if(G->vert[j]==v)
    		{
    			return j;
    		}
    	}
    }
     
    //创建无向图
    int creatUDG(adjMatrix *G){
    	int i,j,k,weight;
    	vertemData v1,v2;
    	printf("请输入图的顶点数和弧数:\n");
    	scanf("%d %d",&G->vertNum,&G->arcNum);
    	for(i=0;i<G->vertNum;i++)
    		for(j=0;j<G->vertNum;j++)
    			G->adj[i][j] = 0;	
    	for(i=0;i<G->vertNum;i++)
    	{
    		printf("请输入图的顶点%d:\n",i+1);
    		scanf("%d",&G->vert[i]);
    	}
    	for(k=0;k<G->arcNum;k++){
    		printf("请输入弧%d的两个顶点:\n",k);
    		scanf("%d %d",&v1,&v2);
    		i = locateVertem(G,v1);
    		j = locateVertem(G,v2);
    		G->adj[i][j] = 1;
    		G->adj[j][i] = 1;
    	}
    	printf("\n无向图存储完毕!");	
    	return 0;
    }
     
    int main(){
    	adjMatrix G;
    	creatUDG(&G);
    	return 0;
    }
    

    运行结果

    (2) 用一个数组去存储已访问点的信息

    01234567
    00000000

    0代表该点未访问,1代表该点访问了。

    (3)深度遍历的算法描述

    • 更新访问点数组信息,在邻接矩阵当中找到该访问点的最近的邻接点,访问该邻接点,输出遍历顶点信息,重复此步骤。
    • 当无法继续深度遍历的时候,在访问点数组中往后依次退1,直到能有一个顶点能继续深度遍历。
    • 当起点都无法继续深度遍历的时候,对图的深度遍历已完成

    实际就是从第n个顶点开始、标记该顶点已被访问,然后查找该顶点第一个未访问的邻接点第i个顶点,再去第i个顶点 深度遍历。

    实际就是一个递归的过程。

    //深度遍历无向图
    void depth_first_traversal_UDG(adjMatrix *G,int *v,int n)
    {
    	int i;
    	if(G==NULL) return;
    	if(n<0||n>G->vertNum) return;
    	v[n] = 1;
    	if(n==0) printf("%d",G->vert[n]);
    	else printf("->%d",G->vert[n]);
    	for(i=0;i<G->vertNum;i++)
    		if(G->adj[n][i]!=0&&v[i]!=1) depth_first_traversal_UDG(G,v,i);	
    }

    (4)完整代码 

    #include<stdio.h>
    #include<stdlib.h>
    #define MAX_VERTEM_NUM 10 
    #define INFINITY 32768
     
    typedef enum{
    	DG,DN,UDG,UDN	
    }graghKind;
    //digraph  DG有向图
    //directed network  DN有向网
    //undirected graph  UDG无向图
    //undirected network  UDN无向网
     
    typedef int vertemData;
     
    typedef struct {
    	vertemData vert[MAX_VERTEM_NUM];			//顶点向量
    	int adj[MAX_VERTEM_NUM][MAX_VERTEM_NUM];	//邻接矩阵
    	int vertNum,arcNum;							//图的顶点数和弧数
    	graghKind gragh;							//图的类型
    }adjMatrix;
     
    //求顶点位置
    int locateVertem(adjMatrix *G,vertemData v){
    	for(int j=0;j<G->vertNum;j++)
    	{
    		if(G->vert[j]==v)
    		{
    			return j;
    		}
    	}
    }
     
    //创建无向图
    int creatUDG(adjMatrix *G){
    	int i,j,k,weight;
    	vertemData v1,v2;
    	printf("请输入图的顶点数和弧数:\n");
    	scanf("%d %d",&G->vertNum,&G->arcNum);
    	for(i=0;i<G->vertNum;i++)
    		for(j=0;j<G->vertNum;j++)
    			G->adj[i][j] = 0;	
    	for(i=0;i<G->vertNum;i++)
    	{
    		printf("请输入图的顶点%d:\n",i);
    		scanf("%d",&G->vert[i]);
    	}
    	for(k=0;k<G->arcNum;k++){
    		printf("请输入弧%d的两个顶点:\n",k);
    		scanf("%d %d",&v1,&v2);
    		i = locateVertem(G,v1);
    		j = locateVertem(G,v2);
    		G->adj[i][j] = 1;
    		G->adj[j][i] = 1;
    	}
    	printf("\n无向图存储完毕!\n\n");	
    	return 0;
    }
    
    //深度遍历无向图
    void depth_first_traversal_UDG(adjMatrix *G,int *v,int n)
    {
    	int i;
    	if(G==NULL) return;
    	if(n<0||n>G->vertNum) return;
    	v[n] = 1;
    	if(n==0) printf("%d",G->vert[n]);
    	else printf("->%d",G->vert[n]);
    	for(i=0;i<G->vertNum;i++)
    		if(G->adj[n][i]!=0&&v[i]!=1) depth_first_traversal_UDG(G,v,i);	
    }
    
    int main(){
    	adjMatrix *G = (adjMatrix*)malloc(sizeof(adjMatrix));
    	creatUDG(G);
    	int visited[G->vertNum]={0};
    	printf("深度优先遍历无向图:\n");
    	depth_first_traversal_UDG(G,visited,0);
    	return 0;
    }

    运行结果

    二、广度优先遍历

    1、简介

    广度优先搜索是指按照广度方向搜索,它类似于树的按层次遍历,是树的按层次遍历的推广。

    基本思想(通俗)

    把一层的邻接点访问完后再去访问下一层。

    2、举例说明

    这是要广度遍历的无向图

    对无向图进行广度优先遍历:

    v1->v2->v3->v4->v5->v6->v7->v8 

    3、C语言代码

    (1)算法描述

    当访问到n层的时候,依次入队列,出队列的顶点访问其邻接点并入队列。

    (2)完整代码

    #include<stdio.h>
    #include<stdlib.h>
    #define MAX_VERTEM_NUM 10 
    
    typedef enum{
    	DG,DN,UDG,UDN	
    }graghKind;
    //digraph  DG有向图
    //directed network  DN有向网
    //undirected graph  UDG无向图
    //undirected network  UDN无向网
    
    typedef int vertemData;
    int visited[MAX_VERTEM_NUM] = {0};//访问数组
    
    /*邻接矩阵*/
    typedef struct {
    	vertemData vert[MAX_VERTEM_NUM];			//顶点向量
    	int adj[MAX_VERTEM_NUM][MAX_VERTEM_NUM];	//邻接矩阵
    	int vertNum,arcNum;							//图的顶点数和弧数
    	graghKind gragh;							//图的类型
    }adjMatrix;
    
    /*队列结构*/
    typedef struct QNode
    {
    	vertemData data;
    	struct QNode *next;
    }QNode;
    
    typedef struct
    {
    	QNode *front,*rear;  //队头队尾指针 
    }LinkQueue;
    
    /*求顶点位置*/
    int locateVertem(adjMatrix *G,vertemData v){
    	for(int j=0;j<G->vertNum;j++)
    	{
    		if(G->vert[j]==v)
    		{
    			return j;
    		}
    	}
    }
    
    /*创建无向图*/
    int creatUDG(adjMatrix *G){
    	int i,j,k,weight;
    	vertemData v1,v2;
    	printf("请输入图的顶点数和弧数:\n");
    	scanf("%d %d",&G->vertNum,&G->arcNum);
    	for(i=0;i<G->vertNum;i++)
    		for(j=0;j<G->vertNum;j++)
    			G->adj[i][j] = 0;	
    	for(i=0;i<G->vertNum;i++)
    	{
    		printf("请输入图的顶点%d:\n",i);
    		scanf("%d",&G->vert[i]);
    	}
    	for(k=0;k<G->arcNum;k++){
    		printf("请输入弧%d的两个顶点:\n",k);
    		scanf("%d %d",&v1,&v2);
    		i = locateVertem(G,v1);
    		j = locateVertem(G,v2);
    		G->adj[i][j] = 1;
    		G->adj[j][i] = 1;
    	}
    	printf("\n无向图存储完毕!\n\n");	
    	return 0;
    }
    
    /*创建空队列*/
    int init_queue(LinkQueue *L)
    {
    	L->front=L->rear=(QNode*)malloc(sizeof(QNode));
    	if(!L->front) return 0;
    	L->front->next=NULL;
    	return 0;	
    }
    
    /*判断队列是否为空*/
    int empty_queue(LinkQueue *L)
    {
    	if(L->front->next==NULL) return 0;
    	else return 1;
    }
    
    /*入队列*/
    int in_queue(LinkQueue *L,int n)
    {
    	QNode *t = (QNode*)malloc(sizeof(QNode));
    	if(!t) exit(0);
    	t->data = n;
    	t->next = NULL;
    	L->rear->next = t;
    	L->rear = t;
    	free(t);
    	return 0;
    }
    
    /*出队列*/
    int out_queue(LinkQueue *L,int *n)
    {
    	QNode *t;
    	if(L->front==L->rear) return 0;
    	t = L->front->next;
    	*n = t->data;
    	L->front->next = t->next;
    	if(t==L->rear) L->rear = L->front;
    	return 1;
    }
    
    /*广度遍历*/
    int BFS_traverse_UDN(adjMatrix *G)
    {
    	int i=0,j;
    	LinkQueue *L = (LinkQueue*)malloc(sizeof(LinkQueue));
    	init_queue(L);
    	printf("广度遍历无向图:");
    	visited[i] = 1;
    	printf("%d",G->vert[i]);
    	in_queue(L,i);
    	do
    	{
    		out_queue(L,&i);
    		for(j=0;j<G->vertNum;j++)
    		{
    			if(G->adj[i][j]!=0&&visited[j]!=1)
    			{
    				visited[j] = 1;
    				printf("->%d",G->vert[j]);
    				in_queue(L,j);
    			}
    		}
    	}while(empty_queue(L));	
    	free(L);
    	return 0;
    }
    
    int main()
    {
    	adjMatrix *G = (adjMatrix*)malloc(sizeof(adjMatrix));
    	creatUDG(G);
    	BFS_traverse_UDN(G);
    	return 0;
    }
    

    展开全文
  • 知识点和思路,参考文章如链接,可直接看原博文:树的广度优先遍历和深度优先遍历,Java实现二叉树的深度优先遍历和广度优先遍历算法示例 1. 广度优先遍历 1.1 原理 英文缩写为BFS,即Breadth FirstSearch。 ...

    目录

    1. 广度优先遍历

    1.1 原理

    1.2 代码示例

    2. 深度优先遍历

    2.1 原理

    2.2 代码示例


    【写在前面】

    今天对广度优先遍历和深度优先遍历做了一些了解和汇总,这里做个学习笔记,便于后续深入学习。知识点和思路,参考文章如链接,可直接看原博文:树的广度优先遍历和深度优先遍历Java实现二叉树的深度优先遍历和广度优先遍历算法示例

    1. 广度优先遍历

    1.1 原理

    英文缩写为BFS,即Breadth FirstSearch。

    是以广度为优先的,一层一层搜索下去的,就像病毒感染,扩散性的传播下去。其过程检验来说是对每一层节点依次访问,访问完一层进入下一层,而且每个节点只能访问一次。

    广度优先遍历树,需要用到队列(Queue)来存储节点对象, 队列的特点就是先进先出。

    先往队列中插入左节点,再插右节点,这样出队就是先左节点后右节点了。

    1.2 代码示例

    (1)树节点:TreeNode

    //树节点
    package MySearchMethod;
    
    public class TreeNode{
        Integer val;
        TreeNode left;
        TreeNode right;
    
        public TreeNode(Integer val){
            this.val = val;
            left = null;
            right = null;
        }
    }

    (2)二叉树:BinaryTree

    //构建二叉树
    package MySearchMethod;
    
    public class BinaryTree<T> {
        private TreeNode root;
        BinaryTree(){
            root = null;
        }
    
        public void insertTreeNode(Integer val){
            if(root == null){
                root = new TreeNode(val);
                return;
            }
            TreeNode currentNode = root;
            while(true){
                if(val.compareTo(currentNode.val) > 0){
                    if(currentNode.right == null){
                        currentNode.right = new TreeNode(val);
                        break;
                    }
                    currentNode = currentNode.right;
                }else {
                    if(currentNode.left == null){
                        currentNode.left = new TreeNode(val);
                        break;
                    }
                    currentNode = currentNode.left;
                }
            }
        }
        public TreeNode getRoot(){
            return root;
        }
    }
    

    (3)广度优先遍历:

    注:下方遍历方法应该也适用于其他树,不只二叉树。待研究。

    /**
     * 广度优先遍历,非递归
     */
    
    package MySearchMethod;
    
    import java.util.LinkedList;
    import java.util.Queue;
    
    public class BreadthFirstSearch {
    
        public static void main(String[] args){
            BinaryTree<Integer> tree = new BinaryTree<>();
            tree.insertTreeNode(35);
            tree.insertTreeNode(20);
            tree.insertTreeNode(15);
            tree.insertTreeNode(16);
            tree.insertTreeNode(29);
            tree.insertTreeNode(28);
            tree.insertTreeNode(30);
            tree.insertTreeNode(40);
            tree.insertTreeNode(50);
            tree.insertTreeNode(45);
            tree.insertTreeNode(55);
    
            System.out.println("广度优先算法(非递归):");
            breadthSearch(tree.getRoot());
    
        }
    
       public static void breadthSearch(TreeNode root){
           Queue<TreeNode> queue = new LinkedList<TreeNode>();
           queue.offer(root);
           TreeNode currentNode;
           while(!queue.isEmpty()){
               currentNode = queue.poll();
               System.out.print(currentNode.val + "  ");
    
               if(currentNode.left != null){
                   queue.offer(currentNode.left);
               }
               if(currentNode.right != queue){
                   queue.offer(currentNode.right);
               }
           }
       }
    }
    

    遍历结果:

    有一个空指针异常的报错,还没找到原因。待补。

    广度优先算法(非递归):
    35  20  40  15  29  50  16  28  30  45  55  Exception in thread "main" java.lang.NullPointerException
        at MySearchMethod.BreadthFirstSearch.breadthSearch(BreadthFirstSearch.java:37)
        at MySearchMethod.BreadthFirstSearch.main(BreadthFirstSearch.java:27)

    Process finished with exit code 1

    2. 深度优先遍历

    2.1 原理

    英文缩写为DFS,即Depth First Search。

    其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。

    深度优先遍历各个节点,需要使用到栈(Stack)这种数据结构。stack的特点是是先进后出。整个遍历过程如下:先往栈中压入右节点,再压左节点,这样出栈就是先左节点后右节点了。

    对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。要特别注意的是,二叉树的深度优先遍历比较特殊,可以细分为先序遍历、中序遍历、后序遍历。

    这里先演示先序遍历的情况。

    2.2 代码示例

    (1)树节点:TreeNode。 如上,略。

    (2)二叉树:BinaryTree。如上,略。

    (3)深度优先遍历:

    注:这里先演示先序遍历的情况。其他情况待补。

    /**
     * 深度优先遍历,非递归
     */
    package MySearchMethod;
    
    import java.util.Stack;
    
    public class DepthFirstSearch {
        public static void main(String[] args){
            BinaryTree<Integer> tree = new BinaryTree<>();
            tree.insertTreeNode(35);
            tree.insertTreeNode(20);
            tree.insertTreeNode(15);
            tree.insertTreeNode(16);
            tree.insertTreeNode(29);
            tree.insertTreeNode(28);
            tree.insertTreeNode(30);
            tree.insertTreeNode(40);
            tree.insertTreeNode(50);
            tree.insertTreeNode(45);
            tree.insertTreeNode(55);
    
            System.out.println("深度优先算法之先序遍历(非递归):");
            depthSearch(tree.getRoot());
        }
    
        public static void depthSearch(TreeNode root){
            Stack<TreeNode> stack = new Stack<TreeNode>();
            stack.push(root);
            TreeNode currentNode;
            while (!stack.isEmpty()){
                currentNode = stack.pop();
                System.out.print(currentNode.val + "  ");
                if(currentNode.right != null){
                    stack.push(currentNode.right);
                }
                if(currentNode.left != null){
                    stack.push(currentNode.left);
                }
            }
        }
    }
    

    遍历结果:

    深度优先算法之先序遍历(非递归):
    35  20  15  16  29  28  30  40  50  45  55  
    Process finished with exit code 0
     

    展开全文
  • 无向图建立、深度优先遍历和广度优先遍历实现算法[借鉴].pdf
  • 深度优先遍历算法之二叉树一、什么是深度优先遍历二、二叉树1. 二叉树简介2.二叉树类型3.二叉树相关术语4. 二叉树的节点代码5. 二叉树遍历顺序6.深度优先遍历和广度优先遍历三、面试题+励志 这不就是二叉树吗?嗯,...
  • 文章目录一、邻接矩阵存储图的深度优先遍历过程分析二、结果分析三、C语言编程实现图的深度优先遍历四、图的遍历及其应用 一、邻接矩阵存储图的深度优先遍历过程分析 对图1这样的无向图,要写成邻接矩阵,则就是...
  • 深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath First Search)是图论中两种非常重要的算法,生产上广泛用于拓扑排序,寻路(走迷宫),搜索引擎,爬虫等,也频繁出现在 leetcode,高频面试题中。...
  • 深度优先遍历

    千次阅读 2020-11-04 21:19:42
    深度优先遍历 思想: 对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。 二叉树的深度优先遍历 二叉树的深度优先遍历分为前序遍历,中序遍历和后续遍历。 前序遍历:先访问根,在访问左...
  • 什么是深度优先遍历 深度优先遍历可定义如下:首先访问出发点v,并将其标记为已访问过;然后依次从v出发搜索v的每个邻接点w。若w未曾访问过,则以w为新的出发点继续进行深度优先遍历,直至图中所有和源点v有路径...
  • 什么是深度/广度优先遍历(DFS/BFS)? 深度优先遍历简称DFS(Depth First Search),广度优先遍历简称BFS(Breadth First Search),它们是遍历图当中所有顶点的两种方式。
  • 二叉树深度优先遍历解题思路

    千次阅读 多人点赞 2021-12-18 10:14:21
    对二叉树的深度遍历的理解和所延伸出的解题思路的分享,作者能力有限,有任何问题请多多指正。
  • Java深度优先遍历和广度优先遍历邻接矩阵算法-数据结构和算法-07-图

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 99,553
精华内容 39,821
关键字:

深度优先遍历