深度优先搜索和广度优先搜索的深入讨论
深度优先搜索和广度优先搜索的深入讨论

一、深度优先搜索和广度优先搜索的深入讨论(一)深度优先搜索的特点是:(1)从上面几个实例看出,可以用深度优先搜索的方法处理的题目是各种各样的。
有的搜索深度是已知和固定的,如例題2-4, 2-5, 2-6;有的是未知的,如例题2-7、例题2-8;有的搜索深度是有限制的,但达到目标的深度是不定的。
但也看到,无论问题的内容和性质以及求解要求如何不同,它们的程序结构都是相同的, 即都是深度优先算法(一)和深度优先算法(二)中描述的算法结构,不相同的仅仅是存储结点数据结构和产生规则以及输出要求。
(2)深度优先搜索法有递归以及非递归两种设计方法。
一般的,当搜索深度较小、问题递归方式比较明显时,用递归方法设计好,它可以使得程序结构更简捷易懂。
当搜索深度较大时,如例题2-5、2-6。
当数据量较大时,由于系统堆栈容量的限制,递归容易产生溢出,用非递归方法设计比较好。
(3)深度优先搜索方法有广义和狭义两种理解。
广义的理解是,只要最新产生的结点(即深度最大的结点)先进行扩展的方法,就称为深度优先搜索方法。
在这种理解情况下,深度优先搜索算法有全部保留和不全部保留产生的结点的两种情况。
而狭义的理解是,仅仅只保留全部产生结点的算法。
本书取前一种广义的理解。
不保留全部结点的算法属于一般的回溯算法范畴。
保留全部结点的算法,实际上是在数掲库中产生一个结点之间的搜索树,因此也属于图搜索算法的范畴。
(4)不保留全部结点的深度优先搜索法,由于把扩展望的结点从数据库中弹出删除,这样,一般在数据库中存储的结点数就是深度值,因此它占用的空间较少,所以,当搜索树的结点较多,用其他方法易产生内存溢出时,深度优先搜索不失为一种有效的算法。
(5)从输出结果可看岀,深度优先搜索找到的第一个解并不一定是最优解。
例如例題2-8得最优解为13,但第一个解却是17。
如果要求出最优解的话,一种方法将是后面要介绍的动态规划法,另一种方法是修改原算法:把原输出过程的地方改为记录过程,即记录达到当前目标的路径和相应的路程值,并与前面已记录的值进行比较,保留其中最优的,等全部搜索完成后,才把保留的最优解输出。
深度优先算法和广度优先算法的时间复杂度

深度优先算法和广度优先算法的时间复杂度深度优先算法和广度优先算法是在图论中常见的两种搜索算法,它们在解决各种问题时都有很重要的作用。
本文将以深入浅出的方式从时间复杂度的角度对这两种算法进行全面评估,并探讨它们在实际应用中的优劣势。
1. 深度优先算法的时间复杂度深度优先算法是一种用于遍历或搜索树或图的算法。
它从图中的某个顶点出发,沿着一条路径一直走到底,直到不能再前进为止,然后回溯到上一个节点,尝试走其他的路径,直到所有路径都被走过为止。
深度优先算法的时间复杂度与图的深度有关。
在最坏情况下,深度优先算法的时间复杂度为O(V+E),其中V表示顶点的数量,E表示边的数量。
2. 广度优先算法的时间复杂度广度优先算法也是一种用于遍历或搜索树或图的算法。
与深度优先算法不同的是,广度优先算法是从图的某个顶点出发,首先访问这个顶点的所有邻接节点,然后再依次访问这些节点的邻接节点,依次类推。
广度优先算法的时间复杂度与图中边的数量有关。
在最坏情况下,广度优先算法的时间复杂度为O(V+E)。
3. 深度优先算法与广度优先算法的比较从时间复杂度的角度来看,深度优先算法和广度优先算法在最坏情况下都是O(V+E),并没有明显的差异。
但从实际运行情况来看,深度优先算法和广度优先算法的性能差异是显而易见的。
在一般情况下,广度优先算法要比深度优先算法快,因为广度优先算法的搜索速度更快,且能够更快地找到最短路径。
4. 个人观点和理解在实际应用中,选择深度优先算法还是广度优先算法取决于具体的问题。
如果要找到两个节点之间的最短路径,那么广度优先算法是更好的选择;而如果要搜索整个图,那么深度优先算法可能是更好的选择。
要根据具体的问题来选择合适的算法。
5. 总结和回顾本文从时间复杂度的角度对深度优先算法和广度优先算法进行了全面评估,探讨了它们的优劣势和实际应用中的选择。
通过对两种算法的时间复杂度进行比较,可以更全面、深刻和灵活地理解深度优先算法和广度优先算法的特点和适用场景。
深度优先算法和广度优先算法的时间复杂度

深度优先算法和广度优先算法都是图搜索中常见的算法,它们具有不同的特点和适用场景。
在进行全面评估之前,让我们先来了解一下深度优先算法和广度优先算法的基本概念和原理。
### 1. 深度优先算法(Depth-First Search, DFS)深度优先算法是一种用于遍历或搜索树或图的算法。
其核心思想是从起始顶点出发,沿着一条路径直到末端,然后回溯,继续搜索下一条路径,直到所有路径都被探索。
在实际应用中,深度优先算法常常通过递归或栈来实现。
### 2. 广度优先算法(Breadth-First Search, BFS)广度优先算法也是一种用于遍历或搜索树或图的算法。
其核心思想是从起始顶点出发,依次遍历该顶点的所有相邻顶点,然后再以这些相邻顶点作为起点,继续遍历它们的相邻顶点,以此类推,直到所有顶点都被遍历。
在实际应用中,广度优先算法通常通过队列来实现。
### 3. 深度优先算法和广度优先算法的时间复杂度在实际应用中,我们经常需要对算法的时间复杂度进行分析。
针对深度优先算法和广度优先算法,它们的时间复杂度并不相同。
- 深度优先算法的时间复杂度:O(V + E),其中V为顶点数,E为边数。
在最坏的情况下,如果采用邻接矩阵来表示图的话,深度优先算法的时间复杂度为O(V^2);如果采用邻接表来表示图的话,时间复杂度为O(V + E)。
- 广度优先算法的时间复杂度:O(V + E),其中V为顶点数,E为边数。
无论采用邻接矩阵还是邻接表表示图,广度优先算法的时间复杂度都是O(V + E)。
### 4. 个人理解和观点在实际应用中,我们在选择使用深度优先算法还是广度优先算法时,需要根据具体的问题场景来进行选择。
如果要寻找图中的一条路径,或者判断两个节点之间是否存在路径,通常会选择使用深度优先算法;如果要寻找最短路径或者进行层次遍历,通常会选择使用广度优先算法。
深度优先算法和广度优先算法都是非常重要的图搜索算法,它们各自适用于不同的场景,并且具有不同的时间复杂度。
深度优先搜索和广度优先搜索

二、 重排九宫问题游戏
在一个 3 乘 3 的九宫中有 1-8 的 8 个数及一个空格随机摆放在其中的格子里。如下面 左图所示。现在要求实现这样的问题:将该九宫调整为如下图右图所示的形式。调整规则是: 每次只能将与空格(上,下或左,右)相临的一个数字平移到空格中。试编程实现。
|2|8 |3|
|1|2|3|
from = f; to = t; distance = d; skip = false; } } class Depth { final int MAX = 100; // This array holds the flight information. FlightInfo flights[] = new FlightInfo[MAX]; int numFlights = 0; // number of entries in flight array Stack btStack = new Stack(); // backtrack stack public static void main(String args[]) {
下面是用深度优先搜索求解的程序:
// Find connections using a depth-first search. import java.util.*; import java.io.*; // Flight information. class FlightInfo {
String from; String to; int distance; boolean skip; // used in backtracking FlightInfo(String f, String t, int d) {
int dist; FlightInfo f; // See if at destination. dist = match(from, to); if(dist != 0) {
深度优先搜索和广度优先搜索

深度优先搜索和⼴度优先搜索 深度优先搜索和⼴度优先搜索都是图的遍历算法。
⼀、深度优先搜索(Depth First Search) 1、介绍 深度优先搜索(DFS),顾名思义,在进⾏遍历或者说搜索的时候,选择⼀个没有被搜过的结点(⼀般选择顶点),按照深度优先,⼀直往该结点的后续路径结点进⾏访问,直到该路径的最后⼀个结点,然后再从未被访问的邻结点进⾏深度优先搜索,重复以上过程,直⾄所有点都被访问,遍历结束。
⼀般步骤:(1)访问顶点v;(2)依次从v的未被访问的邻接点出发,对图进⾏深度优先遍历;直⾄图中和v有路径相通的顶点都被访问;(3)若此时图中尚有顶点未被访问,则从⼀个未被访问的顶点出发,重新进⾏深度优先遍历,直到图中所有顶点均被访问过为⽌。
可以看出,深度优先算法使⽤递归即可实现。
2、⽆向图的深度优先搜索 下⾯以⽆向图为例,进⾏深度优先搜索遍历: 遍历过程: 所以遍历结果是:A→C→B→D→F→G→E。
3、有向图的深度优先搜索 下⾯以有向图为例,进⾏深度优先遍历: 遍历过程: 所以遍历结果为:A→B→C→E→D→F→G。
⼆、⼴度优先搜索(Breadth First Search) 1、介绍 ⼴度优先搜索(BFS)是图的另⼀种遍历⽅式,与DFS相对,是以⼴度优先进⾏搜索。
简⾔之就是先访问图的顶点,然后⼴度优先访问其邻接点,然后再依次进⾏被访问点的邻接点,⼀层⼀层访问,直⾄访问完所有点,遍历结束。
2、⽆向图的⼴度优先搜索 下⾯是⽆向图的⼴度优先搜索过程: 所以遍历结果为:A→C→D→F→B→G→E。
3、有向图的⼴度优先搜索 下⾯是有向图的⼴度优先搜索过程: 所以遍历结果为:A→B→C→E→F→D→G。
三、两者实现⽅式对⽐ 深度优先搜索⽤栈(stack)来实现,整个过程可以想象成⼀个倒⽴的树形:把根节点压⼊栈中。
每次从栈中弹出⼀个元素,搜索所有在它下⼀级的元素,把这些元素压⼊栈中。
并把这个元素记为它下⼀级元素的前驱。
“八”数码问题的宽度优先搜索与深度优先搜索

“八”数码问题的宽度优先搜索与深度优先搜索我在观看视频和查看大学课本及网上搜索等资料才对“八”数码问题有了更进一步的了解和认识。
一、“八”数码问题的宽度优先搜索步骤如下:1、判断初始节点是否为目标节点,若初始节点是目标节点则搜索过程结束;若不是则转到第2步;2、由初始节点向第1层扩展,得到3个节点:2、3、4;得到一个节点即判断该节点是否为目标节点,若是则搜索过程结束;若2、3、4节点均不是目标节点则转到第3步;3、从第1层的第1个节点向第2层扩展,得到节点5;从第1层的第2个节点向第2层扩展,得到3个节点:6、7、8;从第1层的第3个节点向第2层扩展得到节点9;得到一个节点即判断该节点是否为目标节点,若是则搜索过程结束;若6、7、8、9节点均不是目标节点则转到第4步;4、按照上述方法对下一层的节点进行扩展,搜索目标节点;直至搜索到目标节点为止。
二、“八”数码问题的深度优先搜索步骤如下:1、设置深度界限,假设为5;2、判断初始节点是否为目标节点,若初始节点是目标节点则搜索过程结束;若不是则转到第2步;3、由初始节点向第1层扩展,得到节点2,判断节点2是否为目标节点;若是则搜索过程结束;若不是,则将节点2向第2层扩展,得到节点3;4、判断节点3是否为目标节点,若是则搜索过程结束;若不是则将节点3向第3层扩展,得到节点4;5、判断节点4是否为目标节点,若是则搜索过程结束;若不是则将节点4向第4层扩展,得到节点5;6、判断节点5是否为目标节点,若是则搜索过程结束;若不是则结束此轮搜索,返回到第2层,将节点3向第3层扩展得到节点6;7、判断节点6是否为目标节点,若是则搜索过程结束;若不是则将节点6向第4层扩展,得到节点7;8、判断节点7是否为目标节点,若是则结束搜索过程;若不是则将节点6向第4层扩展得到节点8;9、依次类推,知道得到目标节点为止。
三、上述两种搜索策略的比较在宽度优先搜索过程中,扩展到第26个节点时找到了目标节点;而在深度优先搜索过程中,扩展到第18个节点时得到了目标节点。
广度优先搜索(BFS)与深度优先搜索(DFS)的对比及优缺点

⼴度优先搜索(BFS)与深度优先搜索(DFS)的对⽐及优缺点 深搜,顾名思义,是深⼊其中、直取结果的⼀种搜索⽅法。
如果深搜是⼀个⼈,那么他的性格⼀定倔得像头⽜!他从⼀点出发去旅游,只朝着⼀个⽅向⾛,除⾮路断了,他绝不改变⽅向!除⾮四个⽅向全都不通或遇到终点,他绝不后退⼀步!因此,他的姐姐⼴搜总是嘲笑他,说他是个⼀根筋、不撞南墙不回头的家伙。
深搜很讨厌他姐姐的嘲笑,但⼜不想跟⾃⼰的亲姐姐闹⽭盾,于是他决定给姐姐讲述⾃⼰旅途中的经历,来改善姐姐对他的看法。
他成功了,⽽且只讲了⼀次。
从那以后他姐姐不仅再没有嘲笑过他,⽽且连看他的眼神都充满了赞赏。
他以为是⾃⼰路上的各种英勇征服了姐姐,但他不知道,其实另有原因…… 深搜是这样跟姐姐讲的:关于旅⾏呢,我并不把⽬的地的风光放在第⼀位,⽽是更注重于沿路的风景,所以我不会去追求最短路,⽽是把所有能通向终点的路都⾛⼀遍。
可是我并不知道往哪⾛能到达⽬的地,于是我只能每到⼀个地⽅,就向当地的⼈请教各个⽅向的道路情况。
为了避免重复向别⼈问同⼀个⽅向,我就给⾃⼰规定:先问北,如果有路,那就往北⾛,到达下⼀个地⽅的时候就在执⾏此规定,如果往北不通,我就再问西,其次是南、东,要是这四个⽅向都不通或者抵达了终点,那我回到上⼀个地⽅,继续探索其他没去过的⽅向。
我还要求⾃⼰要记住那些帮过他的⼈,但是那些给我帮倒忙的、让我⽩费⼒⽓的⼈,要忘记他们。
有了这些规定之后,我就可以⼤胆的往前⾛了,既不⽤担⼼到不了不⽬的地,也不⽤担⼼重复⾛以前的路。
哈哈哈……深搜优缺点优点1、能找出所有解决⽅案2、优先搜索⼀棵⼦树,然后是另⼀棵,所以和⼴搜对⽐,有着内存需要相对较少的优点缺点1、要多次遍历,搜索所有可能路径,标识做了之后还要取消。
2、在深度很⼤的情况下效率不⾼ ⼴搜,顾名思义,是多管齐下、⼴撒⽹的⼀种搜索⽅法 如果⼴搜是⼀个⼈,那么她⼀定很贪⼼,⽽且喜新厌旧!她从⼀点出发去旅游,先把与起点相邻的地⽅全部游览⼀遍,然后再把与她刚游览过的景点相邻的景点全都游览⼀边……⼀直这样,直⾄所有的景点都游览⼀遍。
广度优先搜索和深度优先搜索有何区别

广度优先搜索和深度优先搜索有何区别在计算机科学和算法领域中,广度优先搜索(BreadthFirst Search,简称 BFS)和深度优先搜索(DepthFirst Search,简称 DFS)是两种常见且重要的图或树的遍历算法。
它们在解决各种问题时都有着广泛的应用,但在搜索策略和特点上存在着显著的差异。
让我们先来了解一下广度优先搜索。
想象一下你正在一个迷宫中,你从入口开始,先探索与入口相邻的所有房间,然后再依次探索这些相邻房间相邻的房间,以此类推。
这就是广度优先搜索的基本思路。
广度优先搜索是以逐层的方式进行的。
它首先访问起始节点,然后依次访问起始节点的所有邻接节点,接着再访问这些邻接节点的邻接节点,就像在平静的湖面上泛起的层层涟漪。
这种搜索方式确保在访问更深层次的节点之前,先访问同一层次的所有节点。
在实现广度优先搜索时,通常会使用一个队列(Queue)数据结构。
将起始节点入队,然后循环取出队列头部的节点,并将其未访问过的邻接节点入队,直到队列为空。
这种方式保证了搜索的顺序是按照层次进行的。
广度优先搜索的一个重要应用是在寻找最短路径问题上。
因为它先访问距离起始节点近的节点,所以如果存在最短路径,它往往能够更快地找到。
例如,在地图导航中,要找到从一个地点到另一个地点的最短路线,广度优先搜索就可能是一个不错的选择。
接下来,我们看看深度优先搜索。
如果说广度优先搜索是逐层展开,那么深度优先搜索就像是一个勇敢的探险家,沿着一条路径一直走下去,直到走到尽头或者无法继续,然后才回溯并尝试其他路径。
深度优先搜索通过递归或者使用栈(Stack)来实现。
从起始节点开始,不断深入访问未访问过的邻接节点,直到无法继续,然后回溯到上一个未完全探索的节点,继续探索其他分支。
深度优先搜索在探索复杂的树形结构或者处理递归问题时非常有用。
比如在检查一个表达式是否合法、遍历一个复杂的文件目录结构等方面,深度优先搜索能够发挥其优势。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一、深度优先搜索和广度优先搜索的深入讨论(一)深度优先搜索的特点是:(1)从上面几个实例看出,可以用深度优先搜索的方法处理的题目是各种各样的。
有的搜索深度是已知和固定的,如例题2-4,2-5,2-6;有的是未知的,如例题2-7、例题2-8;有的搜索深度是有限制的,但达到目标的深度是不定的。
但也看到,无论问题的内容和性质以及求解要求如何不同,它们的程序结构都是相同的,即都是深度优先算法(一)和深度优先算法(二)中描述的算法结构,不相同的仅仅是存储结点数据结构和产生规则以及输出要求。
(2)深度优先搜索法有递归以及非递归两种设计方法。
一般的,当搜索深度较小、问题递归方式比较明显时,用递归方法设计好,它可以使得程序结构更简捷易懂。
当搜索深度较大时,如例题2-5、2-6。
当数据量较大时,由于系统堆栈容量的限制,递归容易产生溢出,用非递归方法设计比较好。
(3)深度优先搜索方法有广义和狭义两种理解。
广义的理解是,只要最新产生的结点(即深度最大的结点)先进行扩展的方法,就称为深度优先搜索方法。
在这种理解情况下,深度优先搜索算法有全部保留和不全部保留产生的结点的两种情况。
而狭义的理解是,仅仅只保留全部产生结点的算法。
本书取前一种广义的理解。
不保留全部结点的算法属于一般的回溯算法范畴。
保留全部结点的算法,实际上是在数据库中产生一个结点之间的搜索树,因此也属于图搜索算法的范畴。
(4)不保留全部结点的深度优先搜索法,由于把扩展望的结点从数据库中弹出删除,这样,一般在数据库中存储的结点数就是深度值,因此它占用的空间较少,所以,当搜索树的结点较多,用其他方法易产生内存溢出时,深度优先搜索不失为一种有效的算法。
(5)从输出结果可看出,深度优先搜索找到的第一个解并不一定是最优解。
例如例题2-8得最优解为13,但第一个解却是17。
如果要求出最优解的话,一种方法将是后面要介绍的动态规划法,另一种方法是修改原算法:把原输出过程的地方改为记录过程,即记录达到当前目标的路径和相应的路程值,并与前面已记录的值进行比较,保留其中最优的,等全部搜索完成后,才把保留的最优解输出。
二、广度优先搜索法的显著特点是:(1)在产生新的子结点时,深度越小的结点越先得到扩展,即先产生它的子结点。
为使算法便于实现,存放结点的数据库一般用队列的结构。
(2)无论问题性质如何不同,利用广度优先搜索法解题的基本算法是相同的,但数据库中每一结点内容,产生式规则,根据不同的问题,有不同的内容和结构,就是同一问题也可以有不同的表示方法。
(3)当结点到跟结点的费用(有的书称为耗散值)和结点的深度成正比时,特别是当每一结点到根结点的费用等于深度时,用广度优先法得到的解是最优解,但如果不成正比,则得到的解不一定是最优解。
这一类问题要求出最优解,一种方法是使用后面要介绍的其他方法求解,另外一种方法是改进前面深度(或广度)优先搜索算法:找到一个目标后,不是立即退出,而是记录下目标结点的路径和费用,如果有多个目标结点,就加以比较,留下较优的结点。
把所有可能的路径都搜索完后,才输出记录的最优路径。
(4)广度优先搜索算法,一般需要存储产生的所有结点,占的存储空间要比深度优先大得多,因此程序设计中,必须考虑溢出和节省内存空间得问题。
(5)比较深度优先和广度优先两种搜索法,广度优先搜索法一般无回溯操作,即入栈和出栈的操作,所以运行速度比深度优先搜索算法法要快些。
总之,一般情况下,深度优先搜索法占内存少但速度较慢,广度优先搜索算法占内存多但速度较快,在距离和深度成正比的情况下能较快地求出最优解。
因此在选择用哪种算法时,要综合考虑。
决定取舍。
二、基本搜索算法比较和搜索算法的优化搜索算法是利用计算机的高性能来有目的的穷举一个问题的部分或所有的可能情况,从而求出问题的解的一种方法。
搜索过程实际上是根据初始条件和扩展规则构造一棵解答树并寻找符合目标状态的节点的过程。
所有的搜索算法从其最终的算法实现上来看,都可以划分成两个部分──控制结构和产生系统,而所有的算法的优化和改进主要都是通过修改其控制结构来完成的。
现在主要对其控制结构进行讨论,因此对其产生系统作如下约定:FunctionExpendNode(Situation:Tsituation;ExpendWayNo:Integer):TSituation;表示对给出的节点状态Sitution采用第ExpendWayNo种扩展规则进行扩展,并且返回扩展后的状态。
(本文所采用的算法描述语言为类Pascal。
)第一部分基本搜索算法一、回溯算法回溯算法是所有搜索算法中最为基本的一种算法,其采用了一种“走不通就掉头”思想作为其控制结构,其相当于采用了先根遍历的方法来构造解答树,可用于找解或所有解以及最优解。
具体的算法描述如下:(略)范例:一个M*M的棋盘上某一点上有一个马,要求寻找一条从这一点出发不重复的跳完棋盘上所有的点的路线。
评价:回溯算法对空间的消耗较少,当其与分枝定界法一起使用时,对于所求解在解答树中层次较深的问题有较好的效果。
但应避免在后继节点可能与前继节点相同的问题中使用,以免产生循环。
二、深度搜索与广度搜索深度搜索与广度搜索的控制结构和产生系统很相似,唯一的区别在于对扩展节点选取上。
由于其保留了所有的前继节点,所以在产生后继节点时可以去掉一部分重复的节点,从而提高了搜索效率。
这两种算法每次都扩展一个节点的所有子节点,而不同的是,深度搜索下一次扩展的是本次扩展出来的子节点中的一个,而广度搜索扩展的则是本次扩展的节点的兄弟节点。
在具体实现上为了提高效率,所以采用了不同的数据结构.三、初探队与广度优先搜索:1、队的定义:队是特殊的线性表之一,它只允许在队的一端插入,在队的另一端删除。
插入一端叫队尾(T),删除一端叫队首(H),没有任何元素的队叫做空队。
队列遵循"先进先出"原则,排队购物、买票等,就是最常见的队。
2、队的基本操作:(1)队的描述:type queue=array[1..100] of integer;var a:queue; {定义数组}h,d:integer; {队首、队尾指针}(2)初始化(图1):procedure start;beginh:=1; d:=1;end;(3)入队操作(图2):procedure enter;beginread(s); {读入数据}inc(d); {队尾加一}a[d]:=s;end;(4)出队操作(图3):procedure out;begininc(h); {队首加一}a[h]:=0;end;广度优先搜索类似于树的按层次遍历的过程。
它和队有很多相似之处,运用了队的许多思想,其实就是对队的深入一步研究,它的基本操作和队列几乎一样。
第二部分搜索算法的优化一、双向广度搜索广度搜索虽然可以得到最优解,但是其空间消耗增长太快。
但如果从正反两个方向进行广度搜索,理想情况下可以减少二分之一的搜索量,从而提高搜索速度。
范例:移动一个只含字母A和B的字符串中的字母,给定初始状态为(a)表,目标状态为(b)表,给定移动规则为:只能互相对换相邻字母。
请找出一条移动最少步数的办法。
(a)(b)问题分析:从初始状态和目标状态均按照广度优先搜索扩展接点,当达到以下状态时,出现相交点,如图(c),接点序号表示生成顺序。
双向扩展结点:(c)(d)顺序扩展的第8个子结点与逆序扩展得到的第4个子结点就是交互点,问题的最佳路径如图(e)→→→→(e)利用双向搜索对广度搜索算法的改进:1、添加一张节点表,作为反向扩展表。
2、在while循环体中在正向扩展代码后加入反向扩展代码,其扩展过程不能与正向过程共享一个for循环。
3、在正向扩展出一个节点后,需在反向表中查找是否有重合节点。
反向扩展时与之相同。
对双向广度搜索算法的改进:略微修改一下控制结构,每次while循环时只扩展正反两个方向中节点数目较少的一个,可以使两边的发展速度保持一定的平衡,从而减少总扩展节点的个数,加快搜索速度。
二、分支定界分支定界实际上是A*算法的一种雏形,其对于每个扩展出来的节点给出一个预期值,如果这个预期值不如当前已经搜索出来的结果好的话,则将这个节点(包括其子节点)从解答树中删去,从而达到加快搜索速度的目的。
范例:在一个商店中购物,设第I种商品的价格为Ci。
但商店提供一种折扣,即给出一组商品的组合,如果一次性购买了这一组商品,则可以享受较优惠的价格。
现在给出一张购买清单和商店所提供的折扣清单,要求利用这些折扣,使所付款最少。
问题分析:显然,折扣使用的顺序与最终结果无关,所以可以先将所有的折扣按折扣率从大到小排序,然后采用回溯法的控制结构,对每个折扣从其最大可能使用次数向零递减搜索,设A为已打完折扣后优惠的价格,C为当前未打折扣的商品零售价之和,则其预期值为A+a*C,其中a为下一个折扣的折扣率。
如当前已是最后一个折扣,则a=1。
对回溯算法的改进:1、添加一个全局变量BestAnswer,记录当前最优解。
2、在每次生成一个节点时,计算其预期值,并与BestAnswer比较。
如果不好,则调用回溯过程。
三、A*算法A*算法中更一般的引入了一个估价函数f,其定义为f=g+h。
其中g为到达当前节点的耗费,而h表示对从当前节点到达目标节点的耗费的估计。
其必须满足两个条件:1、h必须小于等于实际的从当前节点到达目标节点的最小耗费h*。
2、f必须保持单调递增。
A*算法的控制结构与广度搜索的十分类似,只是每次扩展的都是当前待扩展节点中f值最小的一个,如果扩展出来的节点与已扩展的节点重复,则删去这个节点。
如果与待扩展节点重复,如果这个节点的估价函数值较小,则用其代替原待扩展节点,具体算法描述如下:范例:一个3*3的棋盘中有1-8八个数字和一个空格,现给出一个初始态和一个目标态,要求利用这个空格,用最少的步数,使其到达目标态。
问题分析:预期值定义为h=|x-dx|+|y-dy|。
估价函数定义为f=g+h。
<Type>Node(节点类型)=RecordSitutation:TSituation(当前节点状态);g:Integer;(到达当前状态的耗费)h:Integer;(预计的耗费)f:Real;(估价函数值)Last:Integer;(父节点)End<Var>List(节点表):Array[1..Max(最多节点数)] of Node(节点类型);open(总节点数):Integer;close(待扩展节点编号):Integer;New-S:Tsituation;(新节点)<Init>List<-0;open<-1;close<-0;List[1].Situation<- 初始状态;<Main Program>While (close<open(还有未扩展节点)) and(open<Max(空间未用完)) and(未找到目标节点) doBeginBeginclose:=close+1;For I:=close+1 to open do (寻找估价函数值最小的节点) Beginif List[i].f<List[close].f thenBegin交换List[i]和List[close];End-If;End-For;End;For I:=1 to TotalExpendMethod do(扩展一层子节点)BeginNew-S:=ExpendNode(List[close].Situation,I)If Not (New-S in List[1..close]) then(扩展出的节点未与已扩展的节点重复)BeginIf Not (New-S in List[close+1..open]) then(扩展出的节点未与待扩展的节点重复)Beginopen:=open+1;List[open].Situation:=New-S;List[open].Last:=close;List[open].g:=List[close].g+cost;List[open].h:=GetH(List[open].Situation);List[open].f:=List[open].h+List[open].g;End-IfElse BeginIf List[close].g+cost+GetH(New-S)<List[same].f then(扩展出来的节点的估价函数值小于与其相同的节点)BeginList[same].Situation:= New-S;List[same].Last:=close;List[same].g:=List[close].g+cost;List[same].h:=GetH(List[open].Situation);List[same].f:=List[open].h+List[open].g;End-If;End-Else;End-IfEnd-For;End-While;对A*算法的改进--分阶段A*:当A*算法出现数据溢出时,从待扩展节点中取出若干个估价函数值较小的节点,然后放弃其余的待扩展节点,从而可以使搜索进一步的进行下去。