dfs(深度优先搜索)与bfs(宽度/广度优先搜索)

1.dfs(深度优先搜索)

思想:暴力把所有的路径都搜索出来,它运用了回溯,保存这次的位置,深入搜索,都搜索完了便回溯回来,搜下一个位置,直到把所有最深位置都搜一遍,要注意的一点是,搜索的时候有记录走过的位置,标记完后可能要改回来;

回溯法:是一种搜索法,按条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法;

深度优先搜索举例
模板

int check(参数)
{
    if(满足条件)
        return 1;
    return 0;
}
 
void dfs(int step)
{
        判断边界
        {
            相应操作
        }
        尝试每一种可能
        {
               满足check条件
               标记
               继续下一步dfs(step+1)
               恢复初始状态(回溯的时候要用到)
        }
}   

举例(全排列):

import java.util.Scanner;
public class Permutation  {
	static int n;
	static int[] arr; //排列产生的数组
	static int[] judge;	//标记数字是否已使用
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		arr = new int[n];
		judge = new int[n];
		dfs(0);
	}
	public static void dfs(int step){
		if(step == n){	//判断边界(n个数排列完毕)
			for (int i = 0; i < arr.length; i++) {
				System.out.print(arr[i]+" ");
			}
			System.out.println();
		}else{
			for (int i = 0; i < n; i++) {	//尝试每一种可能(即每次执行到这里所有数都遍历一遍)
				if(judge[i]==0){	//满足check条件(即此分支中该数还没使用过)
					judge[i]=1;	//标记该数在此分支已使用
					arr[step] = i+1;	//此分支在此次递归中获得的数
					dfs(step+1);	//继续下一步dfs
					judge[i] = 0;	//恢复初始状态(回溯的时候要用到)
				}
			}
		}
	}
}

2.bfs(宽度/广度优先搜索)

思想:从某点开始,走四面可以走的路,然后在从这些路,在找可以走的路,直到最先找到符合条件的,这个运用需要用到队列(queue),需要稍微掌握这个才能用bfs。

宽度优先搜索举例
模板

void BFS(){
  初始化队列Q;
  起点S入队;
  标记S已经访问;
  while(Q非空){
    取Q的队首元素U;
    U出队列;
    if(u==目标状态){
      返回结果;
     }
    for(所有与U相邻的元素){
      if(相邻的元素合法 && 未访问){
         入队;
         标记访问;
        }
    }
  }
}

如题:
题目(长草)
【问题描述】
小明有一块空地,他将这块空地划分为 n 行 m 列的小块,每行和每列的长度都为 1。
小明选了其中的一些小块空地,种上了草,其他小块仍然保持是空地。
这些草长得很快,每个月,草都会向外长出一些,如果一个小块种了草,则它将向自己的上、下、左、右四小块空地扩展,这四小块空地都将变为有草的小块。
请告诉小明,k 个月后空地上哪些地方有草。
【输入格式】
输入的第一行包含两个整数 n, m。
接下来 n 行,每行包含 m 个字母,表示初始的空地状态,字母之间没有空格。如果为小数点,表示为空地,如果字母为 g,表示种了草。
接下来包含一个整数 k。
【输出格式】
输出 n 行,每行包含 m 个字母,表示 k 个月后空地的状态。如果为小数点,表示为空地,如果字母为 g,表示长了草。
【样例输入】
4 5
.g…

…g…

2
【样例输出】
gggg.
gggg.
ggggg
.ggg.
【评测用例规模与约定】
对于 30% 的评测用例,2 <= n, m <= 20。
对于 70% 的评测用例,2 <= n, m <= 100。
对于所有评测用例,2 <= n, m <= 1000,1 <= k <= 1000。

考试时我的代码(未使用bfs):

public class Eight {
	//最大达到10的9次方,超时
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int m = in.nextInt();
		String[][] di = new String[n][m];
		for (int i = 0; i < n; i++) {
			String str = in.next();
			for (int j = 0; j < m; j++) {
				di[i][j] = String.valueOf(str.charAt(j));
			}
		}
		int k = in.nextInt();
		long now = System.currentTimeMillis();
		for (int i = 0; i < k; i++) {
			di = zhang(di);
		}
		for (int i = 0; i < di.length; i++) {
			for (int j = 0; j < di[0].length; j++) {
				System.out.print(di[i][j]);
			}
			System.out.println();
		}
		System.err.println(System.currentTimeMillis()-now);
	}
	public static String[][] zhang(String di[][]) {
		String[][] result = new String[di.length][di[0].length];
		for (int i = 0; i < result.length; i++) {
			for (int j = 0; j < result[0].length; j++) {
				if (di[i][j].equals("g")) {
					result[i][j] = "g";
				} else {
					result[i][j] = ".";
				}
			}
		}
		for (int i = 0; i < di.length; i++) {
			for (int j = 0; j < di[0].length; j++) {
				if (di[i][j].equals("g")) {
					if (i >= 1) { // 向上长
						result[i - 1][j] = "g";
					}
					if (i <= di.length-2) {
						result[i + 1][j] = "g";
					}
					if (j >= 1) {
						result[i][j - 1] = "g";
					}
					if (j <= di[0].length-2) {
						result[i][j + 1] = "g";
					}
				}
			}
		}
		return result;
	}

答案代码(运用了bfs):

//	答案方法,最大10的6次方,不超时
	 	static final int[] dx = {1, 0, -1, 0};
	    static final int[] dy = {0, 1, 0, -1};
	    static Scanner sc ;
	    static int[][] vis = new int[1000][1000];
	    static int N, M, K;

	    public static void main(String[] args) throws IOException {
	        sc = new Scanner(System.in);
	        N = sc.nextInt();
	        M = sc.nextInt();
	        sc.nextLine();
	        LinkedList<Block> q = new LinkedList<Block>();
	        for (int i = 0; i < N; i++) {
	            String line = sc.nextLine();
	            for (int j = 0; j < M; j++) {
	                if (line.charAt(j) == 'g') {
	                    q.addLast(new Block(i, j, 0));
	                    vis[i][j] = 1;
	                }
	            }
	        }
	        K = sc.nextInt();
	        long now = System.currentTimeMillis();
	        while (!q.isEmpty()) {
	            Block b = q.removeFirst();
	            int month = b.month;
	            if (month < K) {
	                for (int i = 0; i <= 3; i++) {
	                    int nx = b.i + dx[i];
	                    int ny = b.j + dy[i];
	                    if (0 <= nx && nx < N && 0 <= ny && ny < M && vis[nx][ny] == 0) {
	                        vis[nx][ny] = 1;
	                        q.addLast(new Block(nx, ny, month + 1));
	                    }
	                }
	            }
	        }
	        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
	        for (int i = 0; i < N; i++) {
	            for (int j = 0; j < M; j++) {
	                if (vis[i][j] == 1) writer.write('g');
	                else writer.write('.');
	            }
	            writer.write('\n');
	        }
	        writer.flush();
	        System.err.println(System.currentTimeMillis()-now);
	    }

	    //草地上的一块
	    private static class Block {//只能有一个公共类,因此这里使用private或不写
	        int i;
	        int j;
	        int month;

	        public Block(int i, int j, int month) {//因为类没有参数,所以需要在类中再写一个方法给Block的属性赋值
	            this.i = i;
	            this.j = j;
	            this.month = month;
	        }
	    }

3.dfs和bfs的区别

bfs是用来搜索最短径路的解法是比较合适的
比如求最少步数的解,最少交换次数的解,最快走出迷宫等等,因为bfs搜索过程中遇到的第一个解一定是离最初位置最近的,所以遇到第一个解,一定就是最优解,此时搜索算法可以终止

bfs是浪费空间节省时间,dfs是浪费时间节省空间。
因为dfs要走很多的路径,可能都是没用的,(做有些题目的时候要进行剪枝,就是确定不符合条件的就可以结束,以免浪费时间,否则有些题目会TLE);
而bfs可以走的点要存起来,需要队列,因此需要空间来储存,便是浪费了空间,假设有十层,各个结点有2个子节点,那么储存到第10层就要存 2^10-1 个数据,而dfs只需要存10个数据,但是找到答案的速度相对快一点。
特别要注意的一点,dfs不能走T字型,只能走L型

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值