深度优先搜索
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
毛线:记录着每一个岔路口的选择,可以利用它退回到上一个路口重新选择方向。在编 程中,往往会使用数据结构栈来实现(不明白栈操作的赶快百度一下啦),或者用一维数组 也可以。
口诀:算法的关键,用流程图表示会比较清楚,可惜手头没有 Visio 之类的,哪位同学 有兴趣可以画一个发给我啊。
下面用 4 幅图来说明这个过程:
if (map[yy][xx]==2) { d[step] = dir; System.out.Baidu Nhomakorabearintln("找到出口"); break;
} if (map[yy][xx]!=0) {
//撞墙了或遇到走过的路 continue; } else { //记录当前方向 d[step] = dir; dir = 0; map[y][x] = 3; //堵回头路 x = xx; y = yy; step++; } } while (true); //下面为结果输出 System.out.println("一共走了" + step + "步!");
今天来说说深度优先搜索,属于搜索回朔类的算法,相对比较有趣。 关于这个算法的最早传说出现在古希腊时代:
没落贵族的忒修斯(没落到被选为了祭品),爱上了残暴国王弥诺斯的女儿阿里阿德涅 公主,而未来的岳父却决定把可怜的忒修斯送到克里特岛喂牛头怪。
这个特里克岛可是国王的呕心沥血之作,岛上最著名的建筑就是地下迷宫,据说被扔进 去了谁都绕不出来,就算你运气好,找到正确的路,还有一头凶猛的牛头怪养在必经之路上, 送到岛上作为祭品的人从来没能活着回来。
};
System.out.println("原始地图");
printArray(map);
//初始化地图完毕
int d[] = new int[100]; //记录每一步的方向(毛线)
int step = 0;
//已经走了多少步
int dir = 0;
//方向:1,2,3,4 分别代表右、上、左、下
x = startX; y = startY; map[y][x] = d[0] + 10; for (int i = 0; i <= step; i++) {
if (d[i]==1) x++; if (d[i]==2) y--; if (d[i]==3) x--; if (d[i]==4) y++; map[y][x] = d[i]+10; } printArray(map); } }
第四幅图就不用多上了,按照“右上左下”的优先顺序,很快找到了出口。 练习时间:请使用深度优先搜索原理走完下面的迷宫
注意:每次在岔路口选择都必须按照原则进行(不一定非要是“右上左下”,也可以是“上 下左右”),不能人为的做出判断。
下面是我用画笔绘制的将行走过程模拟图
静态图片可能无法完全说明问题,强烈建议读者自己用画笔来画一画。 经过这个练习,你会发现,使用深度优先算法找到的一般并不是最短的路径,但它保证 可以找到一条路径(如果迷宫没有问题的话),如果你坚持要找最短的,后面的广度优先算 法会满足你的要求,这是后话。现在故事讲了、原理分析了、图也画了,该真刀真枪的上代 码了, 请见下回分解。
这当然不能怪毛线,作为一团没有任何智能的妇女用品,它只能做到这一步了。
那可怜的忒修斯在迷宫中面对每一处岔路,是如何选择正确方向的呢?很遗憾,在大多 数情况下,他并没能做出正确的选择,但也没有像前人一样迷失在迷宫中被饿死,而是最终 找到了牛头怪并铲除了它!人和人的差距为什么这么大捏??因为在出发前,阿里阿德涅公 主在他耳边悄悄的说了一个口诀。
公主送毛线自然不是要教夫君打毛衣,而是指导他:“把线头系于迷宫入口处,一路放线 团,一边进入到迷宫,杀掉牛头怪,再循着线团走出迷宫”。
果然,忒修斯按照公主的方法顺利走到了迷宫中央,经过一番搏斗杀死了牛头怪,并沿 着毛线退出了迷宫。按理说后续应该是个大团圆的结局:忒修斯回到王国迎娶公主,三两年 后国王病逝忒修斯继承王位,于是国王与王后过上了幸福的生活。
输出结果:略,大家可以自己运行一下。
现在我写这些代码算比较轻松了,但回想 N 年前刚学习深度优先时,虽然理论上明白了, 但是代码死活也整合不到一块儿去,动不动就数组下标越界或者死循环,希望大家练习这部 分代码的时候一定要有耐心,再加上细心。
深度优先搜索的应用:第一种应用自然是走迷宫啦,上一章列出的代码是寻找一条有效 的路径,深度优先也可以穷举出迷宫中所有的路径。只需要在每次找到迷宫出口时,不要结 束程序,而继续回溯,就可以继续寻找其他走出迷宫的办法,直到变量 step 为 0,程序退出。 你可以记录每一种走法的路径长度(step),最后得到最优解。
正是这个口诀,揭开了深度优先搜索的全部秘密。
口诀的内容,现在听起来很像游戏秘籍,比如魂斗罗调 30 条命……“上上下下左左右右”!
是的,你没听错,伟大的秘密就隐藏在这么简单的口诀中。
我来给大家解释一下,这个口诀指导了忒修斯进入迷宫后的行走规则,即:每当遇到岔 路需要做出选择时,都优先选择往上走(如果上下左右不直观可以理解为东西南北),如果 无法走【无法走的定义是:此路是死路,或此路上有毛线,代表已经走过了】,下一个选择 是往下走,如果还不行就往左走,再不行就往右走。如果四个方向都不行捏?很简单,说明 这个地儿来错了,顺着毛线退回到上一个岔路口,选择下一个方向再次尝试。(如果你没看 明白一会儿有图例说明)
private static void printArray(int m[][]) { int v = 0; for (int y=0;y<10;y++) { for (int x = 0;x<10; x++) { v = m[y][x]; if (v==0) System.out.print(" "); if (v==1) System.out.print("■■"); if (v==2) System.out.print("XX"); if (v==3) System.out.print(" "); if (v==11) System.out.print("->"); if (v==12) System.out.print("^^"); if (v==13) System.out.print("<-"); if (v==14) System.out.print("vv"); } System.out.println(); }
给出伪代码: 构建地图 定义变量 x,y 代表当前位置,初始值为迷宫入口处 循环开始 A:
尝试下一个方向 如果 (下一个方向==→) xx = x + 1, yy = y 如果 (下一个方向==↑) xx = x , yy = y - 1 如果 (下一个方向==←) xx = x - 1, yy = y 如果 (下一个方向==↓) xx = x , yy = y + 1 如果 (四个方向都尝试过了) 如果栈为空,失败,没有出口,跳出循环 出栈,获取上一次的方向 跳到 A
第一幅图是简单的迷宫,左下角是入口,右上角是出口,接下来为方便演示,我们按照“右 上左下”的优先级顺利来进行探索(即遇到岔路先选择走右面,不行选择上面,然后是左面, 下面)。
图中的线条相当于毛线,记录这每一步的选择
因为先走右边,很快就走到了右下角,右边不能再走了,下一个选择是上边。上到顶发现 右边、上边都不能再走了,选择左边,往前走发现到了死胡同,上下左都不能走,右边是走 过的也不能走,该怎么办呢?
判断地图上的 xx,yy 坐标内容 如果是出口,获胜,跳出循环 如果是墙或走过的路,跳到 A 如果是空地
当前方向入栈 x = xx,y = yy //前进一步 在地图上将 x,y 坐标标记为走过的路 判断结束 循环结束
下面贴出完整代码,比较长,做好心理准备。
public class DFS { //辅助方法:打印地图
唯一的选择就是沿着毛线退回到上一个路口重新做出选择,先退到路口 A,刚才在路口 A 选择的方向是左边,下一个选择是下边,可下边是走过的不能走,只能继续后退。
退到路口 B,刚才在路口 B 选择的是上边,下一个选择是左边,不行,再下一个是下边, 也不行,继续后退。
退到路口 C,刚才在路口 C 选择的是右边,下一个选择是上边,可以走,ok,掉转方向, let's go。
}
public static void main(String[] args) {
//初始化地图,数组中 1 代表墙,0 代表路,2 是出口,3 是走过的路
int[][] map = {
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,2},
{1,0,1,0,1,0,1,1,1,1},
不过古希腊的剧作家个个都是苦大仇深,不爱写大团圆的结尾,一般都要主人公死光光 或落下终身残疾才成。这个故事的详细结局可以自行搜索,反正可怜的算法大师、深度优先 搜索算法的缔造者阿里阿德涅公主最后跳入大海喂鲨鱼了。
这个故事大家在小时候肯定都或多或少的知道,现在我们作为一名算法爱好者,应该考 虑一个在原著中被忽略的很重要的细节:为什么一团毛线,就可以让忒修斯顺利的找到牛 头怪,并能成功返回迷宫出口呢?
终于要说到代码实现了,深度优先搜索的原理不难理解,但要用代码实现,也有点小难度, 下面对各个难点进行说明:
1, 地图(迷宫)的表示 一般可以使用整形二维数组描述地图,0 代表空地,1 代表墙,2 代表出口,3 代表走过的路。 有点浪费内存,但是易于理解。 2, 下一步选择(方向)的表示 一般使用连续的整形,如表示“上下左右”四个方向就可用 1,2,3,4 来代替 3, 记录每一步选择(毛线) 上一章说过,可以使用栈或者一维数组进行记录
int startX = 0, startY = 8; //迷宫的入口坐标
int x = startX ,y = startY;
int xx=0, yy=0;
//开始进入迷宫 do {
dir++; //试探下一个方向 if (dir>4) {
//回朔 if (step==0) {
System.out.println("地图没有出口!"); return; } dir = d[step-1]; //取出上一次的方向 //沿着反方向后退一步 if (dir==1) x--; if (dir==2) y++; if (dir==3) x++; if (dir==4) y--; step--; continue; } //按照方向前进一步 xx = x; yy = y; if (dir==1) xx++; if (dir==2) yy--; if (dir==3) xx--; if (dir==4) yy++;
请大家思考,吃午饭去了,回来再写。
接着讨论这个问题:“为什么一团毛线,就可以让忒修斯顺利的找到牛头怪,并能成功返回 迷宫出口呢?”
事实上,如果只有毛线是不可能的,至少无法保证忒修斯顺利找到牛头怪!
当你在迷宫中遇到讨厌的岔路时,毛线只能让你知道回去该走哪条路,却无法告诉前方 该如何选择。毛线只能保证忒修斯可以全身而退,却无法指引牛头怪的方向。
{1,0,1,0,1,0,1,0,0,1},
{1,1,1,1,1,0,1,1,0,1},
{1,0,0,0,0,0,0,1,0,1},
{1,1,1,1,1,1,0,1,0,1},
{1,1,1,1,1,1,0,1,0,1},
{0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
大家可能看出来了,这实际上是一个挺笨的方法,就是“前进->碰壁->转方向->前进->碰 壁->转方向->前进->碰壁->后退->转方向->前进……”的不断重复,但有了这个口诀和毛线, 忒修斯就算回绕一些弯路,但最终一定能找到牛头怪。
总结时间:忒修斯能安然无恙的进出迷宫、找到目标,靠的是毛线和口诀。
变态的特里克岛,但现在是旅游胜地
按常理忒修斯也难逃厄运,但后来的故事印证了两个亘古不变的真理:1,女人是不可靠 的,女儿也不例外 2,穷小子要翻身,就得靠女人
在忒修斯出发前,小伙子费劲脑汁与阿里阿德涅公主又见了最后一面,在这次历史性的 会晤中,公主送给了穷小伙两件足以挽救其性命的礼物:一团毛线和一把据说唯一能杀死牛 头怪的剑。
口诀:算法的关键,用流程图表示会比较清楚,可惜手头没有 Visio 之类的,哪位同学 有兴趣可以画一个发给我啊。
下面用 4 幅图来说明这个过程:
if (map[yy][xx]==2) { d[step] = dir; System.out.Baidu Nhomakorabearintln("找到出口"); break;
} if (map[yy][xx]!=0) {
//撞墙了或遇到走过的路 continue; } else { //记录当前方向 d[step] = dir; dir = 0; map[y][x] = 3; //堵回头路 x = xx; y = yy; step++; } } while (true); //下面为结果输出 System.out.println("一共走了" + step + "步!");
今天来说说深度优先搜索,属于搜索回朔类的算法,相对比较有趣。 关于这个算法的最早传说出现在古希腊时代:
没落贵族的忒修斯(没落到被选为了祭品),爱上了残暴国王弥诺斯的女儿阿里阿德涅 公主,而未来的岳父却决定把可怜的忒修斯送到克里特岛喂牛头怪。
这个特里克岛可是国王的呕心沥血之作,岛上最著名的建筑就是地下迷宫,据说被扔进 去了谁都绕不出来,就算你运气好,找到正确的路,还有一头凶猛的牛头怪养在必经之路上, 送到岛上作为祭品的人从来没能活着回来。
};
System.out.println("原始地图");
printArray(map);
//初始化地图完毕
int d[] = new int[100]; //记录每一步的方向(毛线)
int step = 0;
//已经走了多少步
int dir = 0;
//方向:1,2,3,4 分别代表右、上、左、下
x = startX; y = startY; map[y][x] = d[0] + 10; for (int i = 0; i <= step; i++) {
if (d[i]==1) x++; if (d[i]==2) y--; if (d[i]==3) x--; if (d[i]==4) y++; map[y][x] = d[i]+10; } printArray(map); } }
第四幅图就不用多上了,按照“右上左下”的优先顺序,很快找到了出口。 练习时间:请使用深度优先搜索原理走完下面的迷宫
注意:每次在岔路口选择都必须按照原则进行(不一定非要是“右上左下”,也可以是“上 下左右”),不能人为的做出判断。
下面是我用画笔绘制的将行走过程模拟图
静态图片可能无法完全说明问题,强烈建议读者自己用画笔来画一画。 经过这个练习,你会发现,使用深度优先算法找到的一般并不是最短的路径,但它保证 可以找到一条路径(如果迷宫没有问题的话),如果你坚持要找最短的,后面的广度优先算 法会满足你的要求,这是后话。现在故事讲了、原理分析了、图也画了,该真刀真枪的上代 码了, 请见下回分解。
这当然不能怪毛线,作为一团没有任何智能的妇女用品,它只能做到这一步了。
那可怜的忒修斯在迷宫中面对每一处岔路,是如何选择正确方向的呢?很遗憾,在大多 数情况下,他并没能做出正确的选择,但也没有像前人一样迷失在迷宫中被饿死,而是最终 找到了牛头怪并铲除了它!人和人的差距为什么这么大捏??因为在出发前,阿里阿德涅公 主在他耳边悄悄的说了一个口诀。
公主送毛线自然不是要教夫君打毛衣,而是指导他:“把线头系于迷宫入口处,一路放线 团,一边进入到迷宫,杀掉牛头怪,再循着线团走出迷宫”。
果然,忒修斯按照公主的方法顺利走到了迷宫中央,经过一番搏斗杀死了牛头怪,并沿 着毛线退出了迷宫。按理说后续应该是个大团圆的结局:忒修斯回到王国迎娶公主,三两年 后国王病逝忒修斯继承王位,于是国王与王后过上了幸福的生活。
输出结果:略,大家可以自己运行一下。
现在我写这些代码算比较轻松了,但回想 N 年前刚学习深度优先时,虽然理论上明白了, 但是代码死活也整合不到一块儿去,动不动就数组下标越界或者死循环,希望大家练习这部 分代码的时候一定要有耐心,再加上细心。
深度优先搜索的应用:第一种应用自然是走迷宫啦,上一章列出的代码是寻找一条有效 的路径,深度优先也可以穷举出迷宫中所有的路径。只需要在每次找到迷宫出口时,不要结 束程序,而继续回溯,就可以继续寻找其他走出迷宫的办法,直到变量 step 为 0,程序退出。 你可以记录每一种走法的路径长度(step),最后得到最优解。
正是这个口诀,揭开了深度优先搜索的全部秘密。
口诀的内容,现在听起来很像游戏秘籍,比如魂斗罗调 30 条命……“上上下下左左右右”!
是的,你没听错,伟大的秘密就隐藏在这么简单的口诀中。
我来给大家解释一下,这个口诀指导了忒修斯进入迷宫后的行走规则,即:每当遇到岔 路需要做出选择时,都优先选择往上走(如果上下左右不直观可以理解为东西南北),如果 无法走【无法走的定义是:此路是死路,或此路上有毛线,代表已经走过了】,下一个选择 是往下走,如果还不行就往左走,再不行就往右走。如果四个方向都不行捏?很简单,说明 这个地儿来错了,顺着毛线退回到上一个岔路口,选择下一个方向再次尝试。(如果你没看 明白一会儿有图例说明)
private static void printArray(int m[][]) { int v = 0; for (int y=0;y<10;y++) { for (int x = 0;x<10; x++) { v = m[y][x]; if (v==0) System.out.print(" "); if (v==1) System.out.print("■■"); if (v==2) System.out.print("XX"); if (v==3) System.out.print(" "); if (v==11) System.out.print("->"); if (v==12) System.out.print("^^"); if (v==13) System.out.print("<-"); if (v==14) System.out.print("vv"); } System.out.println(); }
给出伪代码: 构建地图 定义变量 x,y 代表当前位置,初始值为迷宫入口处 循环开始 A:
尝试下一个方向 如果 (下一个方向==→) xx = x + 1, yy = y 如果 (下一个方向==↑) xx = x , yy = y - 1 如果 (下一个方向==←) xx = x - 1, yy = y 如果 (下一个方向==↓) xx = x , yy = y + 1 如果 (四个方向都尝试过了) 如果栈为空,失败,没有出口,跳出循环 出栈,获取上一次的方向 跳到 A
第一幅图是简单的迷宫,左下角是入口,右上角是出口,接下来为方便演示,我们按照“右 上左下”的优先级顺利来进行探索(即遇到岔路先选择走右面,不行选择上面,然后是左面, 下面)。
图中的线条相当于毛线,记录这每一步的选择
因为先走右边,很快就走到了右下角,右边不能再走了,下一个选择是上边。上到顶发现 右边、上边都不能再走了,选择左边,往前走发现到了死胡同,上下左都不能走,右边是走 过的也不能走,该怎么办呢?
判断地图上的 xx,yy 坐标内容 如果是出口,获胜,跳出循环 如果是墙或走过的路,跳到 A 如果是空地
当前方向入栈 x = xx,y = yy //前进一步 在地图上将 x,y 坐标标记为走过的路 判断结束 循环结束
下面贴出完整代码,比较长,做好心理准备。
public class DFS { //辅助方法:打印地图
唯一的选择就是沿着毛线退回到上一个路口重新做出选择,先退到路口 A,刚才在路口 A 选择的方向是左边,下一个选择是下边,可下边是走过的不能走,只能继续后退。
退到路口 B,刚才在路口 B 选择的是上边,下一个选择是左边,不行,再下一个是下边, 也不行,继续后退。
退到路口 C,刚才在路口 C 选择的是右边,下一个选择是上边,可以走,ok,掉转方向, let's go。
}
public static void main(String[] args) {
//初始化地图,数组中 1 代表墙,0 代表路,2 是出口,3 是走过的路
int[][] map = {
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,2},
{1,0,1,0,1,0,1,1,1,1},
不过古希腊的剧作家个个都是苦大仇深,不爱写大团圆的结尾,一般都要主人公死光光 或落下终身残疾才成。这个故事的详细结局可以自行搜索,反正可怜的算法大师、深度优先 搜索算法的缔造者阿里阿德涅公主最后跳入大海喂鲨鱼了。
这个故事大家在小时候肯定都或多或少的知道,现在我们作为一名算法爱好者,应该考 虑一个在原著中被忽略的很重要的细节:为什么一团毛线,就可以让忒修斯顺利的找到牛 头怪,并能成功返回迷宫出口呢?
终于要说到代码实现了,深度优先搜索的原理不难理解,但要用代码实现,也有点小难度, 下面对各个难点进行说明:
1, 地图(迷宫)的表示 一般可以使用整形二维数组描述地图,0 代表空地,1 代表墙,2 代表出口,3 代表走过的路。 有点浪费内存,但是易于理解。 2, 下一步选择(方向)的表示 一般使用连续的整形,如表示“上下左右”四个方向就可用 1,2,3,4 来代替 3, 记录每一步选择(毛线) 上一章说过,可以使用栈或者一维数组进行记录
int startX = 0, startY = 8; //迷宫的入口坐标
int x = startX ,y = startY;
int xx=0, yy=0;
//开始进入迷宫 do {
dir++; //试探下一个方向 if (dir>4) {
//回朔 if (step==0) {
System.out.println("地图没有出口!"); return; } dir = d[step-1]; //取出上一次的方向 //沿着反方向后退一步 if (dir==1) x--; if (dir==2) y++; if (dir==3) x++; if (dir==4) y--; step--; continue; } //按照方向前进一步 xx = x; yy = y; if (dir==1) xx++; if (dir==2) yy--; if (dir==3) xx--; if (dir==4) yy++;
请大家思考,吃午饭去了,回来再写。
接着讨论这个问题:“为什么一团毛线,就可以让忒修斯顺利的找到牛头怪,并能成功返回 迷宫出口呢?”
事实上,如果只有毛线是不可能的,至少无法保证忒修斯顺利找到牛头怪!
当你在迷宫中遇到讨厌的岔路时,毛线只能让你知道回去该走哪条路,却无法告诉前方 该如何选择。毛线只能保证忒修斯可以全身而退,却无法指引牛头怪的方向。
{1,0,1,0,1,0,1,0,0,1},
{1,1,1,1,1,0,1,1,0,1},
{1,0,0,0,0,0,0,1,0,1},
{1,1,1,1,1,1,0,1,0,1},
{1,1,1,1,1,1,0,1,0,1},
{0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
大家可能看出来了,这实际上是一个挺笨的方法,就是“前进->碰壁->转方向->前进->碰 壁->转方向->前进->碰壁->后退->转方向->前进……”的不断重复,但有了这个口诀和毛线, 忒修斯就算回绕一些弯路,但最终一定能找到牛头怪。
总结时间:忒修斯能安然无恙的进出迷宫、找到目标,靠的是毛线和口诀。
变态的特里克岛,但现在是旅游胜地
按常理忒修斯也难逃厄运,但后来的故事印证了两个亘古不变的真理:1,女人是不可靠 的,女儿也不例外 2,穷小子要翻身,就得靠女人
在忒修斯出发前,小伙子费劲脑汁与阿里阿德涅公主又见了最后一面,在这次历史性的 会晤中,公主送给了穷小伙两件足以挽救其性命的礼物:一团毛线和一把据说唯一能杀死牛 头怪的剑。