DFS(深度优先搜索)刷题模板(附详细注释)及经典例题 - java语言

DFS的题目一般不给出邻接矩阵,只给出迷宫图
DFS一般用于求图中起点到终点的路径条数
下面以经典例题为例,给大家DFS算法的刷题模板,习题1中DFS算法中附有详细注释,后面的习题中没有

习题1:走字符迷宫-dfs

给一个n行m列的2维的迷宫,'S’表示迷宫的起点,‘T’表示迷宫的终点,
‘#‘表示不能通过的点,’.’ 表示可以通过的点。你需要从’S’出发走到’T’,
每次只能上下左右走动,并且只能进入能通过的点,每个点只能通过一次。
现在要求你求出有多少种通过迷宫的的方案。

输入格式
第一行输入n,m (1≤n,m≤10)表示迷宫大小。
接下来输入n行字符串表示迷宫。

输出格式
输入通过迷宫的方法数。

/*
样例1:
输入
3 3
S..
.#.
..T

输出
2

样例2:
输入
5 5
S.###
#....
#.#.#
#.#.#
#.#.T

输出
1

样例3:
输入
6 6
...S..
.#.##.
......
...##.
.#.#..
.T....

输出
57
*/
import java.util.*;
public class DFS模板 {
	
	static int MAXV=999;//最大顶点数
	
	static int n,m;		 //大小为n*m的地图
	static int[][] map2=new int[MAXV][MAXV]; //二选一,输入全为数字的地图
	static char[][] map=new char[MAXV][MAXV]; //二选一,输入含有字符的地图
	static int[][] v=new int[MAXV][MAXV];	 //判定该点是否被访问过,0为未访问,1为访问过
	
	static int count=0;    //最终结果,count记录从起点到终点的路径条数
	//move数组的元素顺序,会决定遍历时的顺序
	static int move[][] = { {1,0},{-1,0},{0,1},{0,-1}};
	
	public static void main(String[] args) {
		
		Scanner sc=new Scanner(System.in);
		
		//不能在n,m之前写int
		n=sc.nextInt();
		m=sc.nextInt();

		int x=0,y=0;
		for(int i=0;i<n;i++) {
			String s=sc.next();
			for(int j=0;j<s.length();j++) {
				if(s.charAt(j)=='S') {
					x=i;
					y=j;
				}
			}
			map[i]=s.toCharArray();
		}
		
		dfs(x,y);
		
//		如果跑完dfs后还需要进行其他某些操作,就在这里写
		System.out.println(count);
		sc.close();

	}
	
	
	//dfs算法
	public static void dfs(int x,int y) {	//x,y为当前访问的顶点坐标
		//1、进来之后,要干什么
		v[x][y]=1;
		//如果需要进行某些操作,就在这里进行
		
		//2、如果是终点,要干什么
		if(map[x][y]=='T') {	//出现目标态
			//做相应处理
			count++;
			return ;		//到达目标态,return返回上一层
		}
		
		//3、如果不是终点,要往后递归
		//下面对所有从(x,y)出发能达到的分支顶点进行枚举
		for(int i=0;i<4;i++) {
			//不能直接改变传入的参数x,y的值,要另设变量
			int nx=x+move[i][0];
			int ny=y+move[i][1];
			if(check(nx,ny)) {
				//满足条件,进行某种操作,并将新点进行递归
				
				dfs(nx,ny);	
				
//因为路径有多条,因此要将路线和标记恢复成访问前的状态,为遍历下一条路径做准备
				v[nx][ny]=0;
				
			}
		}
		//return;
	}
	
	//边界条件和约束条件的判断,约束条件由实际题目决定
	public static boolean check(int x,int y) {
		//注意是map[x][y]!='#',如果改成map[x][y]=='.',会忽略‘T’的情况
		if(x>=0&&y>=0&&x<n&&y<m&&map[x][y]!='#'&&v[x][y]==0)
			return true;
		else //与约束条件冲突
			return false;
	}

}

得到了DFS的模板之后,再来练习一下下面这道数字迷宫的经典例题
本题已在题中给出数据,只需直接运行即可得到结果

习题2:走数字迷宫-dfs

给定一个M*N的矩阵(二维数组),分别用0和1表示通路和障碍物。
即 0 表示 通路;1 表示 障碍物。
从矩阵的左上角开始,每次只能向右,下,左,上移动位置,不能斜着走。
请给出从入口到出口的所有路线、最短路线、最短路线的长度。

import java.util.*;
public class 走数字迷宫-dfs {
	
	static int m,n,ans=0,l=0;
	static int[][] map = { 
			{0, 0, 1, 1, 1, 1, 1, 1, 1},  
            {1, 0, 0, 0, 0, 0, 0, 0, 1},  
            {1, 0, 1, 1, 0, 1, 1, 0, 1},  
            {1, 0, 1, 0, 0, 1, 0, 0, 1},  
            {1, 0, 1, 0, 1, 0, 1, 0, 1},  
            {1, 0, 0, 0, 0, 0, 1, 0, 1},  
            {1, 1, 0, 1, 1, 0, 1, 1, 1},  
            {1, 0, 0, 0, 0, 0, 0, 0, 0},  
            {1, 1, 1, 1, 1, 1, 1, 1, 0}
			};
	static int v[][];
	
	static String path="";
	static String shortestpath="";
	static int move[][]= {{1,0},{0,1},{0,-1},{-1,0}};
	
	public static void dfs(int x,int y) {
		
		v[x][y]=1;
		l++;

		if(x==m-1&&y==n-1) {
			path=path+"("+x+","+y+")";
			
			if(shortestpath.length()==0||path.length()<shortestpath.length()) {
				shortestpath=path;
			}
			System.out.println(path);
			if(ans==0||l<ans) {
				ans=l;
			}
			return ;
		}

		//temp用于遍历完之后回退到本地址
		//temp的定义一定要在path增长后,否则会出错
		path=path+"("+x+","+y+")->";
		String temp=path;
		
		for(int i=0;i<4;i++) {
			int newx=x+move[i][0];
			int newy=y+move[i][1];
			if(check(newx,newy)) {
				dfs(newx,newy);
				//将路线和标记恢复成上一次的状态
				v[newx][newy]=0;
				path=temp;
				l--;
			}
		}
	}
	
	public static boolean check(int x,int y) {
		if(x>=0&&y>=0&&x<m&&y<n&&v[x][y]==0&&map[x][y]==0) {
			return true;
		}else return false;
	}
	
	public static void main(String[] args) {
		m=9;n=9;
		v=new int[m][n];
		dfs(0,0);
		System.out.println("最短路线为:"+shortestpath);
		System.out.println("最短路线长度为:"+ans);
	}
}

dfs算法除了应用于迷宫类型的题目之外,还可以应用于其他场景,例如下题用dfs算法求排列组合数,此时模板虽不可直接套用,但思想是类似的

习题3:求排列组合数-dfs

假设给定正整数n,求从1到n的一共n个数 (1,2…n)能组合成的所有的三位数排列组合情况及情况个数。(数字可重复使用)
输入描述:输入正整数n
输出描述:将能排列组合成的数从小到大输出,每个数之间换行,最后输出总个数

样例1:
输入:
1
输出:
111
1

样例2:
输入:
3
输出:
111
112
113
121
122
123
……
333
27

/** 

*/

import java.util.*;
public class 求排列组合数dfs {
	
	static int[] a=new int[3];
	static int count=0;
	//参数index指第几次从数组中取数
	public static void dfs(int index,int n) {
		
		if(index==3) {
			count++;
			for(int i=0;i<3;i++) {
				System.out.print(a[i]+" ");
			}
			System.out.println();
			
			//如果省略了下面的if条件,则return 不可缺少
			return ;
		}
//		if(index>=0&&index<=n) {
		for(int i=1;i<=n;i++) {
				a[index]=i;
				dfs(index+1,n);
			}
			
		}
//	}

	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		dfs(0,n);
		System.out.println(count);
		sc.close();
	}
}

仔细看完这道经典例题之后,如果题目中再对输出的数给出某些限制条件,比如能被3整除的数、素数,或者如果把排列组合成的“三位数”改成“四位数”,相信大家也都会做了。

发布了4 篇原创文章 · 获赞 10 · 访问量 1104

猜你喜欢

转载自blog.csdn.net/qq_32022299/article/details/104356730