引入:从今天开始,我们来进行搜索与图论的学习。首先是搜索,分成DFS和BFS,今天主要讲DFS基础。
两种搜索的区别和联系
联系:
- BFS和DFS都可以对整个空间进行遍历
- 结构都像一棵树一样进行搜索
区别:
- DFS:每一步都往深搜,当搜到叶子节点就进行回溯
- BFS:一层一层进行搜索,每一层搜完再搜索下一层
如何入手:
- DFS:回溯、剪枝
- 每个DFS都对应一条搜索树,最重要要考虑顺序进行遍历所有方案
什么是DFS
dfs是一种搜索方式——深度优先搜索,属于图算法的一种,所以DFS都可以建成树的形式进行遍历。主要涉及到递归。
全排列:
思路(DFS详解):
一共有n个位置,从第一个位置开始考虑,思考当前位置可以填哪些数字,a数字在当前组成的顺序中被用过后,就打上标记,当前顺序后面组成的数字将不使用a,保证当前顺序组成的数字中,每个数字只出现一次进行排列。当这条路走完之后,到达叶子节点(第一种组成顺序完成后),回溯到上一个节点,看有没有别的方式往下走,这样一步步走+回溯直到完成。
回溯到上一个节点,看有没有别的方式往下走——>实现办法:把走过的数字做上标记,一条路径走完后,回到上一个节点时,要把原本的点恢复原样。
需要什么
1.需要一个int型的path数组,来存已经确定位置填的值(每一条路径的答案)
2.需要一个boolean型的st数组,来存当前路径已经有哪些数填过了,以此来判断往下走还有哪些数能填
题目-全排列
详解(Java)
import java.util.Scanner;
public class DFS {
static int n = 10;
static int[] path = new int[n];//已经确定位置填的数字
static boolean[] st = new boolean[n];//存当前路径已经填过的数,以此判断接下来哪些数能填
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
dfs(0);
}
public static void dfs(int u){//遍历顺序:从第一个位置开始看填数字,再到第二个位置...n个位置表明当前路径位置已全部填完
if(u==n){
for (int i = 0; i < n; i++) {
System.out.print(path[i]+" ");
}
System.out.println();
return;
}
for (int i = 1; i <= n; i++) {
if(!st[i]){//当前数字没有用过,可以填
path[u] = i;//第u个位置放的数字
st[i] = true;//记录放过的数字
dfs(u+1);//递归到下一个位置
st[i] = false;//恢复原状,重新进行排序:递归一层结束后,回到上一层——>回溯,恢复数字未使用原样,重新排序
}
}
}
}
题目-n皇后
详解方法一:枚举(Java)
思路:以行为搜索顺序,一行一行进行判断,条件都满足再放 ,否则就不放
import java.util.Scanner;
public class N皇后 {
static int n = 20;
static char[][] path = new char[n][n];
static boolean[] col = new boolean[n];//列
static boolean[] dg = new boolean[n];//对角线
static boolean[] udg = new boolean[n];//反对角线
//u行
public static void dfs(int u){
if(u == n){
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.print(path[i][j]);
}
System.out.println();
}
System.out.println();
return;
}else {
for (int i = 0; i < n; i++) {
if((!col[i]) && (!dg[u + i]) && (!udg[n - u + i])){
path[u][i] = 'Q';
col[i] = dg[u + i] = udg[n - u + i] = true;
dfs(u + 1);
col[i] = dg[u + i] = udg[n - u + i] = false;
path[u][i] = '.';
}
}
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
n = input.nextInt();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
path[i][j] = '.';
}
}
dfs(0);
}
}
详解方法一:每个格子进行选择(Java)
思路:每个格子进行条件选择,放或者不放皇后,进行搜索
import java.util.Scanner;
public class Main {
static int n = 20;
static char[][] path = new char[n][n];
static boolean[] row = new boolean[n];//行
static boolean[] col = new boolean[n];//列
static boolean[] dg = new boolean[n];//对角线
static boolean[] udg = new boolean[n];//反对角线
//x行,y列,s皇后数量
public static void dfs(int x,int y,int s){
//临界条件:在行的最后一格时,跳到下一行第一格
if(y == n){
y = 0;
x++;
}
//最后一行
if(x == n){
//判断皇后数是否到了最大数
//皇后数达到了最大数,输出
if(s == n){
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.print(path[i][j]);
}
System.out.println();
}
System.out.println();
}
return;
}
//不放皇后
dfs(x,y + 1,s);
//放皇后
if((!row[x]) && (!col[y]) && (!dg[n + y - x]) && (!udg[y + x])){
path[x][y] = 'Q';
row[x] = col[y] = dg[n + y - x] = udg[x + y] = true;
dfs(x,y + 1,s + 1);
path[x][y] = '.';
row[x] = col[y] = dg[n + y - x] = udg[x + y] = false;
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
n = input.nextInt();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
path[i][j] = '.';
}
}
dfs(0,0,0);
}
}
学习基础概念主要了解DFS和BFS的区别,DFS中的回溯和剪枝,递归以及恢复原样是关键。