算法之深度优先搜索

一、深度优先搜索

什么是深度优先搜索?

深度优先搜索是对先序遍历的推广,我们从某个顶点开始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();
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值