一、深度优先搜索
什么是深度优先搜索?
深度优先搜索是对先序遍历的推广,我们从某个顶点开始v开始处理v,然后递归的遍历所有与v邻接的顶点。
问题:输入一个数,输出1-n的全排列
我们先假设n=3分析一下,设想你手中有1,2,3个球,要放到三个桶里,先把球1放到桶1,然后走到桶2时,你可以放进球2和球3,选择放进球2,走到桶3时,你手中只有球3可以放入。好,到这里第一种情况已经解决,1-2-3,接下来我们收回球3,走回桶2,再收回球2,将球3放入桶2,走到桶3,放入球2,第二种情况也成功了,1-3-2。现在你把3个桶的球都收回,在桶1中放进球2(刚才我们是先放球1),也可以类推得到2-1-3,2-3-1,再把球收回,桶1中放入球3,也可以得到3-1-2,3-2-1,至此,所有可能已经得出。
简单来说,深度优先搜索就是,这个问题有的答案包含多种可能,对每个可能的分支路径深入到不能在深入为止,而且每个节点只能访问一次。比如上面的问题,球放到最后,成功了,此为一种可能,然后返回桶2或桶1,继续尝试。
下面给出上题的代码:
import java.util.Scanner;
public class QuanPaili {
//数组a为1-n个桶
static int[] a = new int[10];
//book数组用来标记球是否还在手上,已经放入桶中为1,在手上为0
static int[] book = new int[10];
static int n;
//深度搜索算法,step表示现在站在第一个桶前
static void dfs(int step) {
if(step == n+1){//如果已经走到n+1个桶,表示前面的n个桶已经放好球
for(int i=1; i<=n; i++){//输出这种可能性
System.out.print(a[i]+" ");
}
System.out.println();
return;
}
//此时站在第step个桶,尝试应该放哪个球
for(int i=1; i<=n; i++){
if(book[i] ==0){//判断这个球是否还在手上,还在
a[step] = i; //放入这个桶中
book[i] =1; //标价为已放入桶
dfs(step+1);//走向下一个桶
book[i]=0;//收回刚才放入的球,否则,不能进行下一次排列尝试
}
}
return;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
dfs(1);
sc.close();
}
}
下面以一个无向图来加深理解,
用深度优先搜索来遍历图中所有节点,步骤如下:
1.从A点开始,有A-B,A-D,先尝试A-B,在B上,有B-C,到C,C已经没有邻接点了,因为B已经访问过,所以这条路径A-B-C已经结束。
2.从C退回到B,因为A,C已经访问过,所以由B再退回A。
3.在A上,因为B已经访问过,所以访问D,到D上,D可以访问E,F,因为顶点ABCDEFGH是按顺序存储,所以先访问E,到E上,有G,H可以访问,先访问G,到G,因为E已经访问过,所以这条路径A-D-E-G结束。
4.从G退回E,在E上,刚才已经访问过G,而H还没访问,所以访问H,在H上,E已经访问过,所以这条路径A-D-E-H结束
5.从H退回E,因为G,H,D均已经访问过,退回D,在D上,因为F还没访问过,所以访问F
6.在F上,因为D已访问过,所以这条路A-D-H结束
7.至此,所有顶点均已访问过,搜索结束。
问题:实现图的深度优先搜索遍历,输出所有顶点
输入:
5 5 //n个顶点 m条边
1 2 //顶点1到2的边
1 3
1 5
2 4
3 5
输出:
1 2 4 3 5
import java.util.Scanner;
public class Shenduyouxiansousuo {
static int n;
static int sum = 0;
static int[] book = new int[101]; //book数组作为标记,判断是顶点是否访问过
static int[][] e = new int[101][101]; //二维矩阵
//深度优先搜索
public static void dfs(int cur){//cur为当前顶点的编号
System.out.print(cur+" ");
sum++; //每访问一个顶点,sum+1
if(sum == n) return;//所有顶点都已经访问过,结束
for(int i=1; i<=n; i++){
//遍历所有顶点,看哪些顶点与当前顶点cur有边连接,且该顶点还未被访问过
if(e[cur][i] == 1 && book[i] == 0){
book[i] = 1;//标记该顶点已被访问过
dfs(i);//从顶点i在出发访问,无向图可以双向访问,
//也就是说最末尾节点可以返回上一个节点,继续尝试
}
}
return;
}
public static void main(String[] args) {
int m,a,b;
Scanner sc = new Scanner(System.in);
n = sc.nextInt(); //读入顶点数
m = sc.nextInt(); //读入边数
//初始化book数组,0表示还没访问过
for(int i =1; i<=n; i++)
book[i] = 0;
//初始化矩阵,规定自己访问自己为0, 假设99999999为正无穷
for(int i=1; i<=n; i++){
for(int j=1; j<=n; j++){
if(i==j) e[i][j] =0;
else e[i][j] = 99999999;
}
}
//读入边数,因为图是无向图,所以从a到b和从b到a是一样的
for(int i=1;i<=m; i++){
a = sc.nextInt();
b = sc.nextInt();
e[a][b] = 1;
e[b][a] = 1;
}
//从1号顶点出发
book[1] = 1; //标记1号顶点已经访问过
dfs(1);
sc.close();
}
}