其实深度搜索和回溯法的区别,在我这里感觉还是很模糊的
总感觉不一样,但是算法的思想是一样的,都是进行不断的尝试,来判断答案的正确性,如果不行的话,我们就回到上一个状态,再进行尝试,如果还是不行,再回去上一个状态…以此类推
最简单的问题就是全排列的问题了,例如leetcode的
46.全排列
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
这里使用的leetcode题解的方法,传送门
package Leetcode;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
*@time 2020年2月29日:下午10:45:58
*@author Weirdo
*@version 1.0
**/
public class N46 {
public static void main(String[] args) {
N46 n=new N46();
int[] nums= {1,2,3};
List<List<Integer>> permute = n.permute(nums);
System.out.println(permute.toString());
}
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
int[] visited = new int[nums.length];
backtrack(res, nums, new ArrayList<Integer>(), visited);
return res;
}
/**
*
* @param res 记录结果
* @param nums 当前数组
* @param tmp 当前结果
* @param visited 记录数字被记录的状态
*/
private void backtrack(List<List<Integer>> res, int[] nums, ArrayList<Integer> tmp, int[] visited) {
if (tmp.size() == nums.length) {
//这里用的是跳出循环的条件
res.add(new ArrayList<>(tmp));
return;
}
for (int i = 0; i < nums.length; i++) {
//这里我们先判断当前的数字是否已经被使用,如果已经被使用,跳过
if (visited[i] == 1) continue;
//如果没有,我们先使用,再标注为已经使用
visited[i] = 1;
tmp.add(nums[i]);
//添加完了之后,我们进行添加下一个
backtrack(res, nums, tmp, visited);
//下一个添加完了,到了这一步,意味着我们到了return条件返回的
//我们就要去掉这个数字,再去掉标记,再进行尝试
//这里可能有很多人会懵,只要我们把代码复制到编译器,打开debug模式,走一遍,看看程序是怎么运行的就行了
visited[i] = 0;
tmp.remove(tmp.size() - 1);
}
}
}
其实之前做蓝桥杯的题目好像做过类似的题目,也是用到的深度搜索
具体见蓝桥杯2016年javaB组
方格填数
如下的10个格子
±-±-±-+
| | | |
±-±-±-±-+
| | | | |
±-±-±-±-+
| | | |
±-±-±-+(如果显示有问题,也可以参看【图1.jpg】)
填入0~9的数字。要求:连续的两个数字不能相邻。 (左右、上下、对角都算相邻)
一共有多少种可能的填数方案?
请填写表示方案数目的整数。 注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
这里的深度搜索是有条件的深度搜索
public class T6 {
//用于记录我们填入的数字
static int[][] datas = new int[3+2][4+2];
//判断当前的空位是否已经填入
static int[] visit = new int[10];
//记录最后的种数
static int result = 0;
public static void main(String[] args) {
// TODO Auto-generated method stub
for(int i=0;i<datas.length;i++){
Arrays.fill(datas[i], -9);
}
dfs(1,2);
System.out.print(result);
}
public static void dfs(int dep,int pos){
if(dep==3&&pos==4){
result ++;
return;
}else{
if(pos<=4){
for(int i=0;i<10;i++){
if(visit[i]==0&&!hasOther(dep,pos,i)){
datas[dep][pos]=i;
visit[i]=1;
dfs(dep,pos+1);
datas[dep][pos]=-9;
visit[i]=0;
}
}
}else{
dfs(dep+1,1);
}
}
}
public static boolean hasOther(int dep,int pos,int num){
boolean result = false;
if(Math.abs(datas[dep+1][pos]-num)==1||
Math.abs(datas[dep][pos+1]-num)==1||
Math.abs(datas[dep+1][pos+1]-num)==1||
Math.abs(datas[dep-1][pos]-num)==1||
Math.abs(datas[dep][pos-1]-num)==1||
Math.abs(datas[dep-1][pos-1]-num)==1||
Math.abs(datas[dep+1][pos-1]-num)==1||
Math.abs(datas[dep-1][pos+1]-num)==1
){
result = true;
}
return result;
}
}
还有39阶的问题,这里应该用的是递归,应该也属于回溯法的一种
小明刚刚看完电影《第39级台阶》。离开电影院的时候,他数了数礼堂前的台阶数,恰好是39级!
站在台阶前,他突然又想着一个问题:
如果我每一步只能迈上1个或2个台阶。先迈左脚,然后左右交替,最后一步是迈右脚,也就是说一共要走偶数步。
那么,上完39级台阶,有多少种不同的上法呢?
请你利用计算机的优势,帮助小明寻找答案。
public class ThreeNineJie {
static int count=0;
/**
*
* @param num 剩下的步数
* @param step 一共走了多少步
* @return
*/
static void ways(int num,int step){
//如果剩下的台阶小于1的话,那么就只能向前走一步
//如果剩下的台阶为2的话,那么我们有两种走法
// 1.向前走两步
// 2.向前走一步,走两次
//如果剩下的台阶小于零,那么此次的走法不符合要求,退回
if(num<0) return;
//如果剩下的台阶等于零,说明递归结束,判断当前的步数是否为偶数
//如果为偶数,那么此次的走法为正确的走法,种数加一
if(num==0 && step%2==0){
count++;
}
//走一步的情况
ways(num-1,step+1);
//走两步的情况
ways(num-2,step+1);
}
public static void main(String[] args) {
ways(9,0);
System.out.println(count);
}
}
总结:
根据,上面的三道题,我们可以大概的得出一个递归(深度搜索、回溯法)的代码模板
方法体(参数){
if(跳出循环的条件){
如果达到了跳出循环的条件,我们怎么操作,一般都是进行记录并进行返回
}
下面就是写,我们没有达到跳出循环的条件,我们怎么操作
}