DFS
食用指南:
对该算法程序编写以及踩坑点很熟悉的同学可以直接跳转到代码模板查看完整代码
只有基础算法的题目会有关于该算法的原理,实现步骤,代码注意点,代码模板,代码误区的讲解
非基础算法的题目侧重题目分析,代码实现,以及必要的代码理解误区
题目描述:
- 给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
输入格式
共一行,包含一个整数 n。
输出格式
按字典序输出所有排列方案,每个方案占一行。
数据范围
1≤n≤7
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
- 题目来源:https://www.acwing.com/problem/content/844/
题目分析:
- 排列顺序是典型的状态题
- 对每一步的动作,都在总体的状态树中分支出来了对应状态子树
- 在状态树中进行查找或遍历所有状态,用的都是DFS
- DFS本身不难,难在题干分析,
DFS的应用也不止于图论
需要通过一些关键词意识到本题采用DFS - 下面我们来讲解图论
算法原理:
模板算法:
- 传送门:递归(暂无)
预计6月底在C语言博客中介绍一下4步写好递归以及如何理解递归
DFS:
1. 存储结构:
- 递归函数 & 递归栈
2. 写作步骤:
- 停止递归的条件
- 当层递归的处理
- 进入下一层递归
- 若有必要,还原一下静态变量
3. 应用场景:
- 状态题
- 图论搜索
- 其余很多意想不到的地方,需要慢慢积累
4. 搭配使用:
- 递归函数参数,一般参数带有递归函数的层数,已经遍历过的数组的下标,将遍历的数组下标等等
- book[]数组,记录状态树上当前状态节点是否已到达过,剪掉状态树上不合理枝干,加快速度
- res变量,记录递归到底时满足题意的状态数目
- 循环:状态树上一个根节点可能含有很多子节点,需要循环进入子树dfs
- 还原,当某层或某子树的递归操作全部完成时,去另一子树中递归时,可能需要抹去原有的修改痕迹
代码实现:
#include <iostream>
using namespace std;
const int N = 7+1;
int n;
int arr[N];
//标记是否出现的Hash数组
int book[N];
void dfs(int x){
//递归终止条件
if(x == n+1){
for(int i=1; i<=n; i++) cout <<arr[i]<<" ";
cout <<endl;
}
//当层循环选择数
for(int i=1; i<=n; i++){
if (!book[i]) {
arr[x] = i;
book[i] = 1;
//进入下一层循环
dfs(x+1);
//一个状态子树遍历完全后还原改动
book[i] = 0;
}
}
}
int main(){
cin >>n;
dfs(1);
}
代码误区:
1. 易错点:
- 终止条件的选择,我们填写n位数,则第n+1层将前n层填写结果输出
- 标记数组忘记还原,当遍历完1开头的数字后,2开头时,134…n又可以使用了
- 开始递归的层数,开始若从0层填写,则写到n-1层结束,n层时开始输出
2. dfs基础就是递归
- 大家一定要学好递归后再dfs
本篇感想:
- 理解递归后,自然会了dfs
- 本篇不是递归专篇,所以只做宏观讲解
- 到刷题阶段时,我们都类似本篇这样短小精干
目前还是模板学习阶段,需要详细讲解原理和细节 - 看完本篇博客,恭喜已登 《筑基境-初期》
距离登仙境不远了,加油