问题提出:

考虑如下图所示的简单图所表示的缅因州的道路系统。在冬天里保持道路通路通畅的唯一方式就是经常扫雪。高速公路部分希望只扫尽可能少的道路上的雪,而确保总是存在连接任何两个乡镇的干净道路。如何才能做到这点呢?

最简单的方式就是一个哈密顿图,形成一个回路,这当然可以。假设一个回路有n个点,则会有n条边;但对于树,n个点对应n-1条边,所以我可以去寻找一颗树即可。有人说那我们不形成一个圈不就是n-1了么?同学,一条线也是树的一种。将问题剥离成一个数学问题就是,如何从一个图中找到树。树有什么特征呢?这就要问什么叫做树了,树就是每对顶点之间存在唯一简单通路的无向图。所以针对这个图中的每对顶点之间也应该是有通路的,即简单连通图。这里有两种方法,即深度优先搜索和宽度优先搜索。

1.深度优先搜索

       此算法的思路就是先找一个主线,然后回头找到每一个点的分支。这就是回溯法,所谓回溯法,就是每一点都有相同的特征,但状态不一样。当从头遍历到尾后,要原路返回,每次返回的那个点状态不一样。仔细想想,这个递归的思想是一致的,用for i=1:N表示遍历每一种状态,用if判断每一种状态所要执行的动作,每次执行的动作模式是一样,即递归,故我们就有了如下代码:
//d为图的邻近矩阵,p为所生成树的邻近矩阵,
//n为图中点的个数,w为要遍历的顶点
void visit(int **d,int **p,int *T,int n,int w)
{cout<<w<<" ";//输出遍历的点T[w]=1;//已经遍历的点,要加进新增的树中for(int i=0;i<n;i++){//遍历每个点的所有状态,即分支if(d[w][i]==1&&T[i]!=1){//如果有通路,且点不在树中,则记录边,同时访问该分支p[w][i]=1;//记录边visit(d,p,T,n,i);//访问该分支}}
}void DFS(int **d,int **p,int n)
{int *T=new int[n];//建立新建树的顶点集memset(T,0,sizeof(int)*n);visit(d,p,T,n,0);//第一个作为初始值,即从第一个点开始遍历
}


即结果为:

2.宽度优先搜索
        宽度优先搜索(广度优先搜索)简单的说,波的传播,一个点开始振动,会带动周边所有的点振动,然后周边的点也以同样的方式扩散。但宽度优先搜索是不往回传,即只向没有振动的点扩散。所以其思想就是:
从图的顶点中任意地选择一个根,然后添加与这个顶点相关联的所有边。在这个阶段所添加的新顶点成为生成树在1层上的顶点。下一步,按顺序访问1层上的每个顶点,只要不产生简单回路,就将与这个顶点相关联的每条边添加到树里。这样就产生了树在2层上的顶点。如此来,直到已经添加了所有顶点。从以上分析,很容易可以看出,这并非回溯可以做到的,所以得按照其本身思路实现。
        那其本身的思路如何去实现呢?我们从一个点扩散开来,把周边的点记录下来,然后再一个点一个点的重复此工作。所以我们需要空间保存所找到的周边的点,然后等待处理,同时把已经找到的点加入到树当中,所以还要一个空间保存树的节点。这样,我们就可以设T为树,L为所找到的周边的点。每次取出L的第一个点来进行处理,同时处理过的点给删除掉;处理过程中,每找到一个邻点,则加入到L的末尾上。这实际上就是队列,所以可以用队列来保存L。当队列中的尚未处理的点没了,则结束了。下面的代码中,是有点问题的,这里并没有采用队列,只是用数组索引,目的是为了免去在L搜索第i个点是否在L中存在,就直接将索引的值用1代替,1代表存在。这样其结果就是处理的点是按照索引由小到大的顺序处理的,不太符合其思想。其代码如下:
void BFS(int **d,int **p,int n)
{int v;int *T=new int[n];//保存已经生成树的节点int *L=new int[n];//保存每一个树顶点的邻居,memset(T,0,sizeof(int)*n);memset(L,0,sizeof(int)*n);int iL=1;//L的初始长度为1T[0]=1;//只包含顶点0的树L[0]=1;//把初始点0放入尚未处理顶点的表L中while(iL!=0){//如果没有要处理的点,即尚未处理的点没了,则停止v=0;while(v<n&&!L[v++]);//寻找尚未处理顶点,由于索引结果多了个1,后面要减掉L[--v]=0;iL--;//删除L中所寻找的点,表示即将寻找此点的所有邻点cout<<v<<" ";//输出即将处理的点for(int i=0;i<n;i++){if(d[v][i]==1&&T[i]!=1&&L[i]!=1){//如果v与i之间相连,且v不在树T和尚未处理集合L中,则记录下来,作尚未处理的点p[v][i]=1;//记录边T[i]=1;//加入到树中L[i]=1;//加入尚未处理点集合L中,等待处理iL++;//L长度要加1}}}
}

运行结果:

实际方案结果:
        现在来分析两者的时间复杂度。两种算法中每个顶点都要和其它的顶点比较一遍,即O(n^2),但要处理的次数是边数e,即n-1,所以两种算法的时间复杂度都为O(n^2)。但两者又有区别,深度优先搜索使用递归,即栈,而宽度搜索使用队列。

3.网络解释:

        下面是网络中的对这两种方法的解释:

3.1

深度优先搜索与广度优先搜索算法有何区别呢?
通常深度优先搜索法不全部保留结点,扩展完的结点从数据库中弹出删去,这样,一般在数据库中存储的结点数就是深度值,因此它占用空间较少。所以,当搜索树的结点较多,用其它方法易产生内存溢出时,深度优先搜索不失为一种有效的求解方法。
  广度优先搜索算法,一般需存储产生的所有结点,占用的存储空间要比深度优先搜索大得多,因此,程序设计中,必须考虑溢出和节省内存空间的问题。但广度优先搜索法一般无回溯操作,即入栈和出栈的操作,所以运行速度比深度优先搜索要快些

3.2

在说两种算法之前先说说什么叫“搜索”:

可能很多人对搜索的想法有点不对,很多人认为搜索是对已知的一棵树或者是已知的图进行搜索,所以我们常常把搜索和遍历给搞混了,但是其实搜索针对的并不是已知的,这并不代表搜索不能用于已知的,搜索一般用于未知的树,或者未知的图,而我们仅仅是知道这个树或图的产生规则。这个时候才会产生深度优先搜索和广度优先搜索。
然后说一下深度优先搜索和广度优先搜索的区别以及适用范围:
广度优先搜索:广度优先搜索是按照树的层次进行的搜索,如果此层没有搜索完成的情况下不会进行下一层的搜索。
深度优先搜索:深度优先搜索是按照树的深度进行搜索的,所以又叫纵向搜索,在每一层只扩展一个节点,直到为树的规定深度或叶子节点为止。这个便称为深度优先搜索。
我先来说说两种算法的不同点。广度优先搜索,适用于所有情况下的搜索,但是深度优先搜索不一定能适用于所有情况下的搜索。因为由于一个有解的问题树可能含有无穷分枝,深度优先搜索如果误入无穷分枝(即深度无限),则不可能找到目标节点。所以,深度优先搜索策略是不完备的。
适用范围:这点很重要,因为知道两者的适用范围对于编程人员很有好处,至少可以少走弯路。(这些都是开个人观点,有缺少的欢迎补充)
广度优先搜索适用范围:在未知树深度情况下,用这种算法很保险和安全。在树体系相对小不庞大的时候,广度优先也会更好些。
深度优先搜索适用范围:刚才说了深度优先搜索又自己的缺陷,但是并不代表深度优先搜索没有自己的价值。在树深度已知情况下,并且树体系相当庞大时,深度优先搜索往往会比广度优先搜索优秀,因为比如8*8的马踏棋盘中,如果用广度搜索,必须要记录所有节点的信息,这个存储量一般电脑是达不到的。然而如果用深度优先搜索的时候却能在一个棋盘被判定出来后释放之前的节点内存。
当让具体情况还是根据具体的实际问题而定,并没有哪种绝对的好。所以,理解这两种算法的本质是关键。
最后我说说关于找最优解的问题,这种问题如果不依靠其他的辅助算法来说,其实对于广度优先搜索和深度优先搜索来说是一样的,说白了找最优解就是个遍历过程,所以没有哪种算法找最优解更好。但是如果有辅助的启发式算法或者别的算法就另当别论了。

3.3

深度优先搜索法的特点是:
(1)从上面几个实例看出,可以用深度优先搜索的方法处理的题目是各种各样的。有的搜索深度是已知和固定的,如例题4—4、4—5、4—6;有的是未知的,如例题4—7、例题4—8,有的搜索深度是有限制的,但达到目标的深度是不定的。但也看到,无论问题的内容和性质以及求解要求如何不同,但它们的程序结构都是相同的,即都是深度优先算法(一)和深度优先算法(二)中描述的算法结构,不相同的仅仅是存储节点数据结构和产生规则以及输出要求。
(2)深度优先搜索法有递归以及非递归两种设计方法。一般的,当搜索深度较小、问题递归形式较明显时,用递归方法设计的较好,它可以使得程序结构更简捷易懂。但当搜索深度较大时,如例题4—5、4—6,当数据量较大时,由于系统堆栈容量的限制,递归易产生溢出,用非递归方法设计比较好。
(3)深度优先搜索方法有广义和狭义两种理解。广义的理解是,只要最新产生的节点(即深度最大的节点)先进行扩展的方法,就称为深度优先搜索方法。在这种理解情况下,深度优先搜索算法有全部保留和不全部保留产生的节点的两种情况。而狭义的理解是,仅仅只保留全部产生节点的算法。本书取前一种广义的理解。不保留全部节点的算法属于一般的回溯算法范畴。保留全部节点的算法,实际上是在数据库中产生一个节点之间的搜索树,因此也属于图搜索算法的范畴。
(4)不保留全部节点的深度优先搜索法,由于把扩展完的节点从数据库中弹出删除,这样,•一般在数据库中存储的节点数就是深度值,因此它占用的空间较少。
所以,当搜索树的节点较多,用其他方法易产生内存溢出时,深度优先搜索法不失为一种有效的算法。
(5)从输出结果可看出,深度优先找到的第一个解并不一定是最优解。例如例题4—8得最优解COST=13,但第一个解却是COST=17。
如果要求出最优解的话,一种方法将是后面要介绍到的动态规划法,另一种方法是修改原算法:把原输出过程的地方改为记录过程,即记录达到当前目标的路径和相应的路程值,并与前面已记录的值进行比较,保留其中最优的,等全部搜索完成后,才把保留的最优解输出。
广度优先搜索法的显著特点是:
(1)在产生新的子节点时,深度越小的节点越先得到扩展,即先产生它的子节点。为使算法便于实现,存放节点的数据库一般用队列的结构。
(2)无论问题性质如何不同,利用广度优先搜索法解题的基本算法是相同的。但数据库中每一节点内容、产生式规则,根据不同的问题,有不同的内容和结构。就是同一问题也可以有不同的表示方法。
(3)当节点到根节点的费用(有的数称为耗散值)和节点的深度成正比时,特别是当每一节点到根节点的费用等于深度时,用广度优先法得到的解是最优解。但如果不成正比,则得到的解不一定是最优解。这一类问题要求出最优解,一种方法是使用后面要介绍的其他方法求解,另外一种方法是改进前面深度(或广度)优先搜索方法:找到一个目标后,不是立即退出,而是记录下目标节点路径和费用。如果有多个目标节点,就加以比较,留下较优的节点。把所有可能的路径都搜索完后,才输出记录的最有路径。
(4)广度优先搜索算法,一般需存储产生的所有节点,占的存储空间要比深度优先大的多。因此程序设计中,必须考虑溢出和节省内存空间的问题。
(5)比较深度优先和广度优先两种搜索法,广度优先搜索法一般无回溯操作,即人栈和出栈的操作,所以运行速度比深度优先搜索法要快些。
总之,一般情况下,深度优先搜索法占内存少但速度较慢,广度优先搜索法占内存较多但速度较快,在距离与深度成正比的情况下能较快地求出最优解。因此在选择用那种算法时,要综合考虑,决定取舍。

生成图-深度优先搜索/宽度优先搜索相关推荐

  1. 广度优先搜索 - 宽度优先搜索 - 横向优先搜索 (breadth-first search,BFS)

    广度优先搜索 - 宽度优先搜索 - 横向优先搜索 (breadth-first search,BFS) 1. 广度优先搜索 - 宽度优先搜索 - 横向优先搜索 (breadth-first searc ...

  2. 广度优先搜索 宽度优先搜索 迷宫问题 最短路径 最少操作 由近及远 队列

    广度优先搜索,也叫宽度优先搜索,从开始状态,到第一次能到达的状态,再从第一次能到达的状态到下一个能到达的状态,直到探索所有可到达的状态,其时间复杂度为O(状态数×转移的方式). 广度优先搜索使用了队列 ...

  3. 迷宫问题 深度优先搜索 广度优先搜索 宽度优先搜索【python】

    文章目录 一.实验内容 二.深度优先搜索和广度优先搜索总结 1.深度优先搜索算法 2.广度优先搜索算法 三.实验代码和用于测试的迷宫 1.实验代码 2.测试迷宫 2.1 maze1.txt 2.2 m ...

  4. 算法笔记01——深度优先搜索(DFS)/宽度优先搜索(BFS)

    深度优先搜索(DFS) 从某个状态开始,不断地转移状态直到无法转移,然后回退到前一步的状态,继续转移到其他状态,如此不断重复,直至找到最终的解.深度优先搜索从最开始的状态出发,遍历所有可以到达的状态. ...

  5. 宽度优先搜索与深度优先搜索

    宽度优先搜索算法顺序:1-2-3-4-5-6-7 深度优先搜索算法顺序:1-2-4-5-3-6-7 宽度优先搜索算法(又称广度优先搜索)BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点 ...

  6. 深度优先搜索与宽度优先搜索

    深度优先搜索 简称:DFS 基本思路 深度优先遍历图的方法是,从图中某顶点v出发: (1)访问顶点v: (2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历:直至图中和v有路径相通的顶点都被访问 ...

  7. ACM算法笔记(十)深度优先搜索与宽度优先搜索

    深度优先搜索 事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次. 下面 ...

  8. 深度优先搜索和宽度优先搜索

    深度优先搜索和宽度优先搜索 bfs和dfs都是遍历图的方法.dfs是不撞南墙不回头,bfs慢慢来,一层一层来. 类型 空间(h为高度) 时间(h为高度) 采用的数据结构 特点 DFS O(h) O( ...

  9. 八数码宽度优先搜索python代码_图之遍历--广度优先遍历

    何为广度优先遍历呢? 广度优先遍历(BFS),又叫宽度优先搜索或横向优先搜索,是从根结点开始沿着树的宽度搜索遍历,将离根节点最近的节点先遍历出来,在继续深挖下去. 基本思想是: 1.从图中某个顶点V0 ...

最新文章

  1. [置顶] 面向业务开发应用
  2. 如何让Git适应敏捷开发流程?
  3. bash: _upvars: `-a2‘: invalid number specifier bash: /dev/null: 权限不够 bas
  4. 小程序背景图片从服务器获取,小程序之背景图片的加载
  5. 几种常见的分布及其性质
  6. python爬虫反爬策略_用Python语言做爬虫有哪些策略可以对抗反爬虫?
  7. Max Points on a Line@LeetCode
  8. C#反射获取 所有字段 及 私有字段
  9. 计算机英语 1000字论文范文,英语论文格式写作 1000字论文格式-免费论文范文
  10. 谈谈RJ45线序的打法及口诀
  11. CRYPTO buuctf 摩斯
  12. Oracle 数据库表空间容量调整(表空间缩容脚本)脚本
  13. 节卡JAKA机械臂培训笔记(偏入门)
  14. MaxCompute(ODPS)一对多连表时实现多行过滤(同样适用于MySQL)
  15. 零碎知识点之一:循环平稳信号
  16. 【荐藏】代谢组学分析系列
  17. 锂离子电池--SEI膜
  18. 牛客网SQL大厂真题二刷小白白话总结(二)用户增长场景(某度信息流)
  19. 产品经理与项目经理的区别?
  20. Windows 10下无法安装 CAD 2013/2014的解决方法

热门文章

  1. keil STM32中sct 分散加载文件学习
  2. android 静音接口,android 静音方法
  3. DirectX游戏开发之3D角色动起(下)
  4. lm283_飞利浦Tango LED泛光灯BVP283-普照网
  5. selenium+python模拟浏览器进入好友QQ空间留言
  6. 跳出“套路”泥沼,让在线教育回归本心
  7. 亚马逊后台操作不容措施旺季~~~~~~
  8. hdu 5984 Pocky
  9. 蓝桥杯 ADV168 算法提高 快乐司机(贪心 快排)(java)
  10. Python中Toplevel