由全排列问题简谈DFS

先贴简单的全排列问题地址 : https://www.luogu.com.cn/problem/P1706 本题说的是n的全排列

本题是最典型的最入门的DFS问题,可采用回溯法。一眼看觉得很简单,第一次动手写发现许多细节问题,也理解了很长时间,所以在此分享一下个人的理解(PS:很多大佬发的题解确实没有仔细地说明缘由和代码的层次问题,我说一下吧)

首先要知道DFS的原理,如果不理解的话一定要多画图,每画一笔都要想想为什么这么画,下一步怎么得出来的。不多说,直接上代码后面分析。

import java.util.Scanner;

public class 全排列_标记法 {
	static Scanner sc = new Scanner(System.in);
	static int n = sc.nextInt();
	static int result[] = new int[n+1];
	static int vis[]  = new int[n+1];
	public static void main(String[] args) {
		DFS(1);//对应下文问题1
	}

	private static void DFS(int step) {
		if(step >= n+1) {//对应问题3
			print();
			return;
		}
		for(int j = 1 ; j <= n ; j++) {//对应问题3
			if(vis[j] == 0) {
				vis[j] = 1;//标记数组,见问题2
				result[step] = j;
				DFS(step+1);
				vis[j] = 0;//回溯,见问题2
			}
		}
	}

	private static void print() {
		for(int i = 1; i <= n; i++) {//对应问题3
			System.out.printf("%5d",result[i]);
		}
		System.out.println();
	}
	
}

对于代码中的一些问题这里说一下。

1.为什么dfs参数从1开始,1是什么意思?

答:我们将题目当成装箱子问题,第一步(step)是选择第一个箱子,然后是第二个箱子,直到把所有的箱子选完并装完数字。要注意的是,每选完一个箱子,就立即选择一个数字装入,再进入dfs循环,选择下一个箱子。

2.这里的vis[]数组怎么理解?

答:vis[]数组的vis -> visit访问,指的是判断访问数组,其数组默认值为0,所以当vis[i]==0时,当作此元素未被访问过(也就是未被标记过),而vis[i]==1时,指此元素已被访问标记过。

3.回溯不知如何层次在哪里,vis[i] = 0有何作用?

答:这个问题,你应该自己去仔细画一遍dfs的图,看看其原理,是什么时候往回退的,一步步往回退的过程就是回溯,过程中将已经访问过的元素重新赋值为0。因为你赋值为0的一个重要作用,就是在你触底,不能在继续访问新元素的时候,要往回退,往回退的过程中还要有另一条路会用到此元素,有种重点理解的地方,这个数字只存在与一个数组中,你访问数字,即为访问数组的元素,而且是只有一个数组,所以要一步步的回溯。例如:从1-4的过程中,当你选择step1:1  ->  step2 : 2 ->  step3 : 3 -> step4 : 4 。此时1234这4个数字都被访问过,vis[i]都为1,如果你要换一种排列,他们都不在访问范围内,所以第一步回溯,将step4中的4 vis[i]重新置为0,然后发现除了4没有其他数字选择,那么再次回溯,将step3的数字的也置为0(不是数字置为0,而是vis[]置为0),此时就有了选择,除了3以外,4也没被访问过,所以第二种方案,为step3为4,然后继续dfs,step4为3.(最后的结果 为 :1243)。

4.step==n+1为什么不能是step==n,循环为何要从int i = 1开始?

答:前者,step==n是可行的,他与dfs函数一开始的位置有关,但是输出可能不尽人意,dfs从1开始,意味着选择了第一个箱子,所以对应的触底反弹的底就是step==n+1。如果你愿意写从第0个箱子开始,也是可以的。

后者问题的答案,在DFS函数中 int i = 1,指的是数字i从1开始,就是题目要求的1-n之间的数字,不能从0开始,这样违反题目规则。在print函数中,其实与前者问题挂钩,我们舍弃了result[0]这个位置,因为主观上我们都是从第一个箱子开始,当然你改成int i = 0也可以,但是的dfs参数和step==n+1也需要改。这三者是互相联系的。

整体理解:切记dfs的顺序,step到底时不代表全部结束,因为此程序跑完会有很多次step==n或者step==n+1的情况。

这是数组标记法,还有一种交换法dfs的方法,我整理后再发!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值