搜索

B F S BFS BFS D F S DFS DFS 的思路

B F S BFS BFS(广度优先搜索)

​ 思路: B F S BFS BFS 类似于树的层次遍历过程,从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。舍去空间换时间(每一个的状态都需要用一个对象存储起来,放到一个队列中去)。

​ 实现:

​ 1.创建一个空队列 q u e u e queue queue(用来存放节点)和一个空列表 v i s i t visit visit(用来存放已访问的节点)

​ 2.依次将起始点及邻接点加入 q u e u e queue queue v i s i t visit visit

​ 3. p o l l poll poll 出队列中最先进入的节点,从图中获取该节点的邻接点

​ 4.如果邻接点不在 v i s i t visit visit 中(这个情况还没有判断),则将该邻接点加入 q u e u e queue queue v i s i t visit visit

​ 5.输出 p o l l poll poll 出的节点

​ 6.重复 3 3 3 4 4 4 5 5 5,直至队列为空

D F S DFS DFS(深度优先搜索)

​ 思路:沿着树的深度遍历树的节点,选一条路一直走到底,回到之前有路的点,换一个方向继续走,知道遍历所有的子节点(所有情况全部判断完成),进而达到全局搜索的目的。

dfs和bfs差别_BFS和DFS之间的区别_culing2941的博客-CSDN博客

全排列

D F S DFS DFS 解法

​ 思路:将此过程看做一棵树,每一个结点下都会有 n n n 个结点表示下一个数,首先先将全部 n n n^n nn 个结果全部得出,然后剪枝,减去有重复数字出现的情况。

public static void dfs(int depth,String ans,int n){//当前深搜的层数,目前的结果,目标层数
    if(depth==n){//当前深搜层数=目标层数
        System.out.println(ans);
        return;
    }
    for(int i=1;i<=n;i++){
    	if(!ans.contains(i+""))//只有当还没有用过i的时候,才会在现在的基础上继续往下拓展
			dfs(depth+1,ans+i;n);//进入下一层,ans记录为进入下一层的值,n不变
    }
}
B F S BFS BFS 解法

思路:先将有重复数字的结果得出,每一个数后都可以跟 n n n 中可能,那么将这 n n n 中可能存入队列中,然后重复此过程,直到字符串的长度为 n n n 时,得到结果;剪枝,如果这个数字已经用过了,就直接只用下一个数字。

public static void bfs(int n){
    Queue<String> queue = new LinkedList<>();
    for(int i=1;i<=n;i++)
        queue.offer(i+"");
    while(!queue.isEmpty()){
        String now = queue.poll();
        for(int i=1;i<=n;i++){//每个结点都向下产生n个结果
            if(now.contains(i+""))//i已经使用过了
                continue;
            String son = now + i;
            if(son.length()==n)
                System.out.println(son);
            else
                queue.offer(son);
        }
    }
}

整数划分

​ 思路:对 n n n 进行划分后, n n n 可以被不超过 n n n 个数累加得到,进行累加的每一个数,也可以被不超过它本身个数累加得到。

public static void dfs(int n,int nowget,int max,String ans){//要划分的数,现在已经得到的值,目前划分已经用到的最大值,具体拆分方法
    if(nowget==n){
        ans = ans.substring(0,ans.length()-1);
        System.out.println(n+"="+ans);
        return;
    }
    for(int i=1;i<=n-nowget;i++){//从nowget累加到n
        if(i>=max)//只有当下一个数不小于我之前用过的最大值时,才能保证整个结果为非递减
            dfs(n,nowget+i,i,ans+i+"+");
    }
}

例题

例题:路径之谜

题目链接:路径之谜 - 蓝桥云课 (lanqiao.cn)

图片描述

​ 思路:

​ 1.从入口点开始,到达每一个点都将对应位置北墙和西墙的箭靶数减一,每一个点,都可以继续向四个方向继续前进(前提是这个点没有走过,在城堡范围内,且这个点对应的两个箭靶的数字不为 0 0 0 )。

​ 2.如果已经到了终点,就要判断现在每一个箭靶上的数字是否都已经变为 0 0 0 ,如果是,那么此时走的路径就是正确解,否则就需要回溯,考虑其他的行走路线。

​ 3.回溯:因为要从已经走过的点退回来,所以在已经走过的点上射的箭要收回,箭靶数加一,并且标记此点为还没有走过。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

public class 路径之谜 {
	static int[] path;//记录最终路径,因为底面为n*n,所以走出需要2*n步
	static int n;
	static int[] cntx;//存储北墙箭靶数字
	static int[] cnty;//存储西墙箭靶数字
	static boolean[][] visited;//判断此点有没有走过
	static int dx[] = {1, 0, -1, 0};//到下一个点x坐标的变化量
	static int dy[] = {0, 1, 0, -1};//到下一个点y坐标的变化量
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
	public static void main(String[] args) throws IOException {
    	n = Integer.parseInt(in.readLine());
    	cntx = new int[n];
    	cnty = new int[n];
    	path = new int[n * n];
    	visited = new boolean[n][n];
    	String[] s = in.readLine().split(" ");
    	for (int i = 0; i < n; i++) {
    		cntx[i] = Integer.parseInt(s[i]);
    	}
    	s = in.readLine().split(" ");
    	for (int i = 0; i < n; i++) {
    		cnty[i] = Integer.parseInt(s[i]);
    	}
    	dfs(0, 0, 0);//从0,0位置开始走,目前走了0步
  }
	private static void dfs(int x, int y, int step) {
		path[step] = y * n + x; //将该点编号记录到路径中
		visited[x][y] = true;//将该点标记为已经走过的状态
		cntx[x]--;//拔掉对应北墙的箭
		cnty[y]--;//拔掉对应西墙的箭
		if (x == n - 1 && y == n - 1 && check()){//判断是否到达终点
			for (int i = 0; i <= step; i++){//输出答案
				System.out.print(path[i]+" ");
			}
			return;
		}
		for (int i = 0; i < 4; i++){//上下左右四个方向搜索下一步
			int xx = x + dx[i], yy = y + dy[i];
             //下一步(xx,yy)未走过且在地图范围内
			if (0 <= xx && xx <= n-1 && yy >= 0 && yy <= n-1&& !visited[xx][yy] ){
				if (cntx[xx] > 0 && cnty[yy] > 0){//该点对应箭靶上有箭,说明该点可以走
					dfs(xx, yy, step + 1);//搜索下一步
                    //要从xx,yy点回来,在xx,yy点射的箭要复原,并重新标记xx,yy点没有走过
					visited[xx][yy] = false;
					cntx[xx]++;
					cnty[yy]++;
				}
			}
		}
	}
	private static boolean check() {//判断到达终点时,是否箭靶数都已经归零
		for (int i = 0; i < n; i++) {
			if (cntx[i] != 0 || cnty[i] != 0)
				return false;
		}
		return true;
	}
}
例题:迷宫

题目链接:迷宫 - 蓝桥云课 (lanqiao.cn)

image-20230521232831206

​ 思路:从起点开始,将从此点能到达的点存储到队列中,每次获取并删除队列中的第一个元素,并将其能到达且还未到达过的点(若此点已经到达过,则表示当前处理的这条路径不是最短路径)存储到队列中,若已经到达终点,则此路径为最短路径。如果队列中已经没有元素,但仍未到达迷宫终点,则表示此迷宫无解

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.Queue;

public class 迷宫 {
	static int num;//存储迷宫最短路径所需要的步数
	static int xsize = 30;//迷宫大小30行50列
	static int ysize = 50;
	static char[][] arr = new char[xsize][ysize];//存储迷宫:0表示路,1表示墙
	static boolean[][] help = new boolean[xsize][ysize];//判断此点是否已经做过
	static int[][] dir = {{1,0},{0,-1},{0,1},{-1,0}};//四个方向横纵坐标的变化量
	static char[] sign = {'D','L','R','U'};//表示四个方向
	public static void main(String[] args) throws IOException {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		for(int i=0;i<xsize;i++){
			arr[i] = in.readLine().toCharArray();
		}
		out.println(bfs());
		out.print(num);//额外输出最短路径需要多少步
		out.flush();
	}
	private static String bfs() {
		Queue<Node> list = new LinkedList<>();//队列
		int x = 0;
		int y = 0;
		int runnum = 0;
		list.add(new Node(x,y,"",runnum));//将起点存储到队列中
		while(!list.isEmpty()){//判断队列是否为空,若为空,则此迷宫没有通路
			Node now = list.poll();//获取队列中的第一个元素并删除
			help[now.x][now.y] = true;//将此点标记为已经走过
			for(int i=0;i<4;i++){//循环四次,对四个方向进行处理
				int xx = now.x + dir[i][0];//移动后的x坐标
				int yy = now.y + dir[i][1];//移动后的y坐标
                  //此点在迷宫范围内,未走过,不是墙
				if(check(xx,yy) && help[xx][yy]==false && arr[xx][yy]=='0'){
					list.add(new Node(xx,yy,now.num + sign[i],now.runnum + 1));//将此点存入队列中
					if(xx==xsize-1 && yy==ysize-1){//如果已经到了迷宫终点
						num = now.runnum + 1;//所需步数+1(now.runnum是到达迷宫终点前一步所需要的步数)
						return now.num + sign[i];//返回通过迷宫的方式
					}
				}
			}
		}
		return "";//空字符串,表示此迷宫无通路
	}
	private static boolean check(int xx, int yy) {//判断此点是否在迷宫范围内
		return xx>=0 && yy>=0 && xx<xsize && yy<ysize;
	}
	static class Node{
		int x;//x坐标
		int y;//y坐标
		int runnum;//到达此点最短步数
		String num;//到达此点的方式
		public Node(int x, int y,String num ,int runnum) {
			super();
			this.x = x;
			this.y = y;
			this.num = num;
			this.runnum = runnum;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值