递归初涉dfs深搜

前言

最近在家学习,路线任务做了个大概,打算多抽出来时间学习算法,应付一下蓝桥杯的比赛。我很菜的,不过看了许多关于dfs的博客自己也研究了一些,特别是今天撞了一下脑袋觉得突然开窍,为了不辜负这几天dfs带给我心灵上的痛苦,写下这篇博客,记录一下。

基本概念

深度优先搜索算法(Depth First Search,简称DFS):一种用于遍历或搜索树或图的算法。 沿着树的深度遍历树的节点,尽可能深的搜索树的分支。

我觉得这就是一条路走到黑,走到头了,再退一步换个方向走到新头,再退回来,再走到新的头。如此反复走到“头”,得到所有的结果。

算法思想

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。
在这里插入图片描述

模板

int check(参数)
{
    if(满足条件)
        return 1;
    return 0;
}
 
void dfs(int step)
{
        if语句判断是否达到边界
        {
            相应操作
        }
        for语句循环每种可能
        {
               满足check条件
               标记
               继续下一步dfs(step+1)
               恢复初始状态(回溯)
        }
}   

搞一些例子

1.【力扣算法】17-电话号码的字母组合

题目

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
在这里插入图片描述

示例

输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

说明

尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

题解

在这里插入图片描述
如上图,有这几种输出。
在这里插入图片描述
示例中的输入 2, 3.。这是分为了两级,需要先进入第一级 2 对应的 a,然后进入第二级,分别与d,e,f 组合。再有 b, c。

代码

class Solution {
    char[][] m = new char[][]{	//new 一个号码映射关系字符数组
        {}, //号码0
        {}, //号码1
        {'a', 'b', 'c'},    //号码2
        {'d', 'e', 'f'},
        {'g', 'h', 'i'},
        {'j', 'k', 'l'},
        {'m', 'n', 'o'},
        {'p', 'q', 'r', 's'},
        {'t', 'u', 'v'},
        {'w', 'x', 'y', 'z'}    //号码9
    };
    public List<String> letterCombinations(String str) { // 在此处接收传入的字符串
        List<String> res = new ArrayList<>();	//new 一个列表,存放答案
        if(str.length() == 0)return res;	//判断输入的字符串长度
        dfs(str, 0, new StringBuilder(), res);	//调用dfs
        //StringBuilder 可以动态的改变字符串的长度,适合用来当做中间的容器
        return res;
    }

    void dfs(String str, int index, StringBuilder sb, List<String> res){
        //截止条件
        if(index == str.length()){
            res.add(sb.toString());	//满足条件就把它放进 res 字符列表中
            return;
        }

        //候选节点
        for(char c : m[str.charAt(index) - '0']){
            sb.append(c);	//先把字符存起来,此时没存够
            dfs(str, index+1, sb, res);	//进行下一级
            sb.deleteCharAt(sb.length() - 1);	//回溯
        }
    }
}

解题思路

在这里插入图片描述
在dfs(1)进行到 dfs(2) 时便暂停后续运行,直接将dfs(1) 所代表的m[2]入栈,运行dfs(2)所代表的m[3],循环三次之后,退栈,此时栈中还有m[2],继续循环
在这里插入图片描述

2.【蓝桥杯】八皇后

描述如下:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法!

在这里插入图片描述

题解

来分析一下,假设想要在黑色方块位置放置一个皇后
在这里插入图片描述
如果一列一列的放置皇后的话,图中黑色位置能放置一个皇后的合法性条件为:
1、绿色线条经过的方格没有皇后 (不处于同一斜线)
2、红色线条经过的方格没有皇后 (不处于同一行)
3、紫色线条经过的方格没有皇后 (不处于同一斜线)

所以假设某一行皇后的位置用列来记录,比如queen[row] = col,意思是第row列的皇后的位置在第col行。
同行的逻辑很好判断,那么我们想要在黑色方块位置放置一个皇后,怎么判断前面几行是否在绿色线条和紫色线条上已经有了皇后呢?思路也很简单:
假设测试放置皇后的方块的位置为n行,nCol行,假设位于它之前列的所在的行是否有皇后位于紫色线或者绿色上,那么就符合下面条件:

//假设此时测试在n行放置一个皇后,n>m
//测试放入n行的列数为 nCol

]//获取m行上皇后所在的列
int mCol = queen[m]//行的差值
int rowDvaule = n - m;

//列的差值
int columnDvalue= n-mCol;

//如果行的差值的绝对值 == 列的差值的绝对值
//那么就是测试放置皇后的方块与之前存在的皇后在同一条斜率为 正负1 的直线上

在这里插入图片描述
所以,判断某个方块是不是可以放置皇后,让它满足以下条件即可

bool available(int row,int col){//传入测试的行和列
	for(int i = 1; i < row; i++){
		if(col == queen[i])return false;//同一列不行
		if((row-i)==(col-queen[i]))return false;//在斜率为1的对角线上
		if((row-i)+(col-queen[i])==0)return false;//同一副对角线拒绝
	}
	//满足以上判断条件,不在同一列,不在同一条直线上 。
	//因为判断的是测试块之前所有行的皇后,所以不需判断测试行与之前存在的皇后是不是在同一行。测试行大于其他皇后所在行
	return true;
}

完整代码

//八皇后递归解法

public class eight {
	static int queen[]= new int[]{-1,-1,-1,-1,-1,-1,-1,-1};
	static int count=0;
	public static void main(String[] args){
		dfs(0);
		System.out.print(count);

	}
	static boolean check(int row,int col){//判断某个皇后是否与已有皇后冲突
		for(int i=0;i<row;i++){
			if(col == queen[i])return false;//同一列不行
			if((row-i)==(col-queen[i]))return false;//在斜率为1的对角线上
			if((row-i)+(col-queen[i])==0)return false;//同一副对角线拒绝
		}
		return true;
	}
	static void dfs(int queenNumber){//在第queenNumber行找能放皇后的位置
		if(queenNumber==8){//如果八个皇后都放满了统计一下
			count++;
			return;
		}
		if(queen[queenNumber] == -1){
			for(int i=0;i<8;i++){//从0~7遍历这一行的八个空位
				if(check(queenNumber,i)){
				//如果可以放这个位置就记录下第queenNumber个皇后的位置
					queen[queenNumber]=i;	
					
					dfs(queenNumber+1);
					queen[queenNumber] = -1;//回溯
					
				}
			}
		}
		
		return;
	}

想法

在这里插入图片描述

后记

这是目前所了解的一点dfs的知识
在这里插入图片描述
我知道 a --> b --> c --> d 然后回去到c 进而有 a --> b --> c --> e
可是,我不明白再怎么回到b,这是困扰我的问题

然后想象栈的机制,明白了他是一层层的 return的,dfs(7)遍历完了之后,退栈,此时又恢复到dfs(6),而dfs(6)现在还有空位呢,就执行空出的,又把dfs(7)入栈,如此反复。就把所有的情况都遍历了一遍

猜你喜欢

转载自blog.csdn.net/EEstefan/article/details/104828991