1:算法描述
2:应用举例
2.1:迷宫问题
迷宫问题,是最能体现深度优先搜索的思想情况之一了,我们先分析以下迷宫问题,迷宫如下图;
图2.1.1
首先,我们顶下搜索策略,我们按照左下右上的方法进行搜索。会有以下情况。
图2.1.2
当一直向左,走到最左边的时候,这个时候该位置左上右下所有位置都已经没办法走了,只能退出当前位置(退出本轮递归),返回如下:
图2.1.3
退回如上图2.1.3中位置,发现该位置的左上右下已经也已经没法继续走了,所以依然退出本轮,回到上次的位置。如下图所
图2.1.4
由于该位置,左边已经走过了,无法走了,所以只能下走,(因为上面走过的路径,要被标记为走过了,无法成功的,不能再走了)。
图2.1.5
代码如下:
/* 使用深度优先搜索算法解决迷宫的问题 */ class MiGong { //迷宫array,1代表出口。 public int[][] miGongArray =new int[][]{ {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,1} }; //迷宫标记数组于迷宫数组等大, //代表该点是不是已经走过了,如果走过了标记为1,如果可以走,则标记为0 public int[][] miGongmarkArray =new int[][]{ {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0} }; //通过px 和 py数组来实现右下左上的移动顺序 public int px[] = {0, 1, 0, -1}; public int py[] = {1, 0, -1, 0}; //找到标志位,0代表没有找到,1代表找到。 public int flag = 0; //构造方法 public MiGong(){ }; //深度搜索的迭代方法 /** * * @param x 输入位置的x坐标 * @param y 输入位置的y坐标 */ //迭代终止条件:如果当前位置是目标坐标,则直接,将找到标志flag置1,然后 return。 //如果经过4次判断,当前位置左上右下都不能走下去,则直接退出本次迭代,回到上次即可。 //迭代逻辑:在当前位置有效前提以及找到flag为0的基础上,在标记array中,将本位置置1,代表已经走过了。 // 然后向左上右下进行迭代判断。 //返回条件:void public void searchDFS(int x,int y){ System.out.println("****************"); for(int[]a:miGongmarkArray) { System.out.println(Arrays.toString(a)); } //如果找到了出口,则flag置1,直接return即可 if(miGongArray[x][y] == 1){ flag = 1; return ; } for(int i=0;i<4;i++){ //分别向左上右下 int newx = x + px[i]; int newy = y + py[i]; //如果位置满足要求,且flag为0,且没有走过,则进行左上右下的DFS if(newx>=0&&newx<miGongArray[0].length&&newy>=0&&newy<miGongArray.length&&flag==0&&miGongmarkArray[newx][newy]==0){ //将markArray数组标记为1,代表一直走过了 miGongmarkArray[newx][newy] = 1; searchDFS(newx,newy); //将markArray数组标记为0,消除上次标记 //miGongmarkArray[x][y] = 0; } } } }
2.2:有序二维数组找值问题
二维序列的每一行都是从小到大,每一列也是从下到大,请在该二维数组中,找到指定数值。如何用深度优先搜索来解决这个问题尼?
首先,我们应该分析该问题的特点?每一行都是从小到大,每一列也是从小到大,这样的话,如果我们从左下角开始寻找,则会发现,所有大的值在该点的右侧,所有小于该点的值在该点上侧,所以我们也可以设计深度优先算法来找到该值。
代码示例:
/* 使用深度优先搜索算法寻找二维数组中的值的问题 本例子 */ class ArraySearch { public int[][]array = new int[][]{ {1,5,9 ,13}, {2,6,10,14}, {3,7,11,15}, {4,8,12,16} }; public int[][]markArray = new int[][]{ {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0} }; public int flag = 0; //思路:通过深度搜索的方法找到目标值,根据当前位置值和目标值之间的关系来判断 //下次搜索的位置,直到找到目标值为止。 //迭代终止条件和处理:当找到目标值时,直接return,或者已经超过边界也没找到, //迭代递归逻辑:当前坐标值如果大于目标值,则向上迭代寻找,如果小于着向右迭代寻找。 //迭代返回:void public void searchArray(int x,int y,int k){ //******迭代结束条件****** //x和y到达边界,可能始终找不到值, if(x<0||y>=array[0].length){ System.out.println("No Found!!"); return ; } //如果找到了目标值,则打印路径,直接return if(array[x][y] == k){ for(int[]a:markArray) { System.out.println(Arrays.toString(a)); } flag = 1; return ; } //******迭代逻辑****** markArray[x][y] = 1; if(array[x][y]>k){ //如果当前值大于目标值,则应向上寻找 searchArray(x-1,y,k); } else{ //如果当前值小于目标值,则应向右寻找{ searchArray(x, y+1,k); } } }
2.3:图的遍历问题
使用深度优先搜索遍历如下图2.3.1的图。
图2.3.1
应该如何操作尼?这取决于我们如何表达图这种数据结构,一般我们采用邻接矩阵的方式表达。没有两两相连的我们认为他们之间的距离是Integer.MAX = 65535,而相连的之间就用正常之间的数值来表示即可!!!所以我们有下图所示的数值和矩阵来表达图这种数据结结构。
class SearchInGraph { public class Vert{ public String lable; public boolean wasVisted = false; public Vert(String lable){ this.lable = lable; } } //顶点数组 Vert n0 = new Vert("n0"); Vert n1 = new Vert("n1"); Vert n2 = new Vert("n2"); Vert n3 = new Vert("n3"); Vert n4 = new Vert("n4"); Vert n5 = new Vert("n5"); Vert n6 = new Vert("n6"); Vert n7 = new Vert("n7"); Vert n8 = new Vert("n8"); public Vert[] nVerts = new Vert[]{n0,n1,n2,n3,n4,n5,n6,n7,n8}; //邻接矩阵 public int[][] array = new int[][]{ {0,1,1,0,0,0,0,0,0},//n0 {1,0,1,1,1,0,0,0,0},//n1 {1,1,0,0,1,1,0,0,0},//n2 {0,1,0,0,1,0,1,0,0},//n3 {0,1,1,1,0,1,1,1,0},//n4 {0,0,1,0,1,0,0,1,0},//n5 {0,0,0,1,1,0,0,1,1},//n6 {0,0,0,0,1,1,1,0,1},//n7 {0,0,0,0,0,0,1,1,0} //n8 }; /** * * @param v 起始顶点编号 */ //深度查询思路:从起始地点开始,按照邻接矩阵中相连顶点前后顺序进行进一步的深度搜索。 //递归终止条件:当所有顶点都被访问过了,则结束本次递归, // 或者当前顶点所有相连顶点都已经标记访问过的了,就可以结束本轮递归了(结束本轮递归,是指本次方法就结束了,不在进行递归下去。) //递归逻辑:在当前位置的为标记的相邻顶点,再次进行DFS //递归返回值:void public void searchDFS(int v){ //递归终止条件:可以包含在递归逻辑里面 //将本节点标记为访问过了。 nVerts[v].wasVisted = true; //打印当前节点 System.out.println(nVerts[v].lable); //在当前节点的相邻顶点,找有效顶点 for(int i=0;i<nVerts.length;i++){ if(nVerts[i].wasVisted==false&&array[v][i]==1){ searchDFS(i); } } } }
2.4:一维数组的全排序问
该案例是用来,获取当前序列的全排列问题的,比如:数字123的全排列共有6个:123,132,213,231,312,321共计6个如何获取尼?
可以可以采用深度搜索的思想。
我们以123为例子,首先假设我们第一个数取的是1,那么要标记1位已经选择了,然后在1,2,3里面再选择一个没有标记的,那先选的是2,同时也要把2标记为已经选择,然后再选择一个没有标记的,则只有3了。本次找到了一个序列123,然后再归来的过程将,3,2解标记,同时再归来到第1次再123中选择时,会进行先选择3的时候。这时候就可以产生132序列了!
/** * 该案例是用来,获取当前序列的全排列问题的。 * 比如:数字123的全排列共有6个:123,132,213,231,312,321共计6个 * 如何获取尼? * 可以可以采用深度搜索的思想 */ class WholeOrderInArray { //原始序列 public int[] array = new int[]{1,2,3,4,5}; //标记序列 public int[] markArray = new int[]{0,0,0,0,0}; //用来判断是不是所有序列都已经标记了的 int sum = 0; //用来存放当前序列的队列,方便我们加入和取出 Stack<Integer> stack = new Stack<>(); //深度搜索思路:暂时选择一个数字,标记为已经选择,然后遍历序列选择一个没有被标记的,组成两个数字的序列。 //不断重读,注意的是,要再回溯的过程,清楚点本轮递归的标记和选择的数据,这是因为我们获得全序列。 //递归终止条件:当前循环已经结束,则递归结束,本轮递归终止条件是没有只有一个数据,加入后,则将stack内数据输出即可。 //起始位置 public void getWholeOrderDFS(){ //*******递归终止条件******* //每次迭代sum都要清零 sum = 0; for(int i:markArray){ sum = sum + i; } //sum = length-1意味着,当前只剩下最后一个数字了,直接加入然后return即可 if(sum == markArray.length){ for(Integer i:stack){ System.out.print(i); } System.out.println(); return ; } //*******递归逻辑******* for(int i=0;i<array.length;i++){ //当前数,没有被标记 if(markArray[i]==0){ markArray[i] = 1; stack.push(array[i]); getWholeOrderDFS(); //再本次递归中结束后,即再回溯的过程,要将本次放进去的数据取出来。为下次选择做准备 stack.pop(); markArray[i] = 0; } } } }
2.5:一维序列的找子序列问题
当前做法不是DFS的做法,但是我相信DFS也可以做,加油加油
2.5.1:重复子序列
2.5.2:顺子子序列
//满足数字中有顺子或者豹子的号码即为靓号 class BeautifulNumber{ //记录豹子和顺子的长度 int baoZhi = 0; int shunZhi = 0; //思路:从头遍历号码,再当前数字处,进行顺子或者豹子的判断, public void judgeValue(int[]number){ int length = number.length; int counter; //从头遍历得到每一个数字, for(int i=0;i<length;i++){ //***********以该数字为起点,进行豹子判断******** int index = i; counter = 1; while(true){ if(index>=length-1){ break; } if(number[index] !=number[index+1]){ break; } else{ counter++; index++; } } //将更长的豹子长度放入baoZhi变量中, if(counter > baoZhi){ baoZhi = counter; } //***********判断顺子。包括递增和递减********** //变量归位 index = i; counter = 1; while(true){ if(index>=length-1){ break; } //如果,满足升序,这对变量操作,继续判断。 if(number[index+1]- number[index] ==1){ counter++; index++; } else{ break; } } //将更长的豹子长度放入baoZhi变量中, if(counter > shunZhi){ shunZhi= counter; } } System.out.println("豹子数长度为:"+baoZhi); System.out.println("顺子数长度为:"+shunZhi); } }
3:总结归纳
- DFS深度优先搜索算法,讲究搜索的过程,就是去那里搜索的,即搜索策略的问题;迷宫里,按照既定方向,二维数组里按照判断大小,图中按照相邻顶点是不是被标记进行判断。这恶是关键的!!!
- 就是有搜索后,标记的过程,只要本位置被搜索过了就要标记住,防止后序搜索中,再重新走过。
- 再全子序列问题中,合适的去标记以及去字符是这个问题解决的关键!!!原因尼?这个不是简单的找到一个就行,而是所有序列都要找到,所以要及时清除标记和取出此次字符,为下次的搜索获取全序列做准备。