深度优先搜索算法(英语:Depth-First-Search,简称DFS)
是一种用于遍历或搜索树或图的算法。 沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点v的那条边的起始节点。整个进程反复进行直到所有节点都被访问为止。属于盲目搜索,最糟糕的情况算法时间复杂度为O(!n)。(Wiki)
(直到走不下去才往回走)
基本模板
int search(int t)
{
if(满足输出条件)
{
输出解;
}
else
{
for(int i=1;i<=尝试方法数;i++)
if(满足进一步搜索条件)
{
为进一步搜索所需要的状态打上标记;
search(t+1);
恢复到打标记前的状态;//也就是说的{回溯一步}
}
}
}
第一部分 DFS+回溯
1.全排列问题(leetcode.46):
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[[1,2,3],[1,3,2], [2,1,3], [2,3,1],[3,1,2],[3,2,1]]
思路分析:
这是一个非常典型的使用 回溯算法 解决的问题。解决回溯问题,一定不要偷懒,拿起纸和笔,画出一个树形结构,思路和代码就会比较清晰了。
方法:回溯算法(深度优先遍历+状态重置)
以示例 [1,2,3] 为例,因为是排列问题,我们只要按顺序选取数字,保证上一层选过的数字不在下一层出现,就能够得到不重不漏的所有排列方法,画出树形结构如下图:
注意:
1、在每一层,我们都有若干条分支供我们选择。由于是排列问题,之前使用过的数字,在下一层中不能再选取,那么从当前层走到下一层的时候,我们就要问一问自己,哪些数字已经使用过。在编码实现中,可以使用一个布尔型数组 used,用于记录之前(当前路径之前的层)哪些数字使用过。
2、在程序执行到上面这棵树的叶子结点的时候,此时递归到底,方法要返回了,对于这个最后一层选取的数,要做两件事情:(1)释放对它的占用;(2)将它从当前选取放进的排列中弹出。当前,在每一层的方法执行完毕,要返回的时候,都需要这么做。这两点可以简单概括为“状态重置”。
代码部分:
代码力求清晰准确,里边的每一步都可以对应基本模板填写。
1.使用stack
package Algorithm.DFS;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
//从叶子节点到根节点形成的一条路径,就是题目要求的一个排列;
//在更深层可选的数一定不能包括在之前层选过的数,所以需要使用一个数组used记录哪些数在之前层选过
//DFS的核心理解是在每一个分叉位置都是一个新i了,所以每一层i是独立的,回溯的时候意味着一个分支走完了
//所以回到分叉位的时候状态要重置
public class FullPermutationTest {
//深度优先遍历
//代码分为两部分 1.声明 2.递归
//声明部分需要声明一个result结果集,一个boolean数组存放状态,
//一个len确认状态数组的长度
public static List<List<Integer>> permute(int[] nums){
List<List<Integer>> result=new ArrayList<>();
if(nums==null||nums.length==0){
return result;
}
int len=nums.length;
boolean[] used=new boolean[len];
makePermution(nums,used,0,new Stack<Integer>(),result);
return result;
}
//终止条件为当前size=数组长度,这时候往result里增加当前stack的值,成为result的一个子集
//递归条件是如果当前位没有用到,塞进stack,然后置用过状态
//然后进一步递归,传cursize+1。递归的主要递归点是cursize,终止条件也是cursize
//回溯 stackpop,used状态回溯,没什么好说的
public static void makePermution(int[] nums,boolean[] used,
int curSize,Stack<Integer> stack,
List<List<Integer>> result){
if(curSize==nums.