C++数据结构与算法分析——DFS(深度优先遍历)

介绍

递归

递归是算法的一种基本思想,它是在程序中不断反复调用自身来达到求解问题的方法,这需要问题具有一个性质:待求解问题可以分解为相同的若干个子问题,这样它才能调用自身。

递归例题

题目描述

输入一个整数n,请你编写一个函数,int fact(int n),计算并输出n的阶乘。

输入格式

共一行,包含一个整数n

输出格式

共一行,包含一个整数表示n的阶乘的值。

数据范围

1 ≤ n ≤ 10 1≤n≤10 1n10

输入样例:
3
输出样例:
6
样例解释

3 ! = 3 ∗ ( 2 ! ) = 3 ∗ 2 ∗ ( 1 ! ) = 3 ∗ 2 ∗ 1 = 6 3! = 3 * (2!) = 3 * 2 * (1!) = 3 * 2 * 1 = 6 3!=3(2!)=32(1!)=321=6

解题思路

从样例解释中很容易可以看出,求 3 ! 3! 3!可以分解为求 3 ∗ ( 2 ! ) 3*(2!) 3(2!),又可以分解为求 3 ∗ 2 ∗ ( 1 ! ) 3*2*(1!) 32(1!),最后返回答案 6 6 6。这正好符合递归的思想,假设要求n的阶乘,那么求相当于要求 n ∗ ( n − 1 ) ! n * (n - 1)! n(n1)!,而当 n = = 1 n == 1 n==1时,则直接返回 1 1 1,由此我们可以写出代码

代码
#include <iostream>
using namespace std;

int fact(int n){
    if(n == 1) return 1;
    return n * fact(n - 1);
}

int main()
{
    int n;
    cin >> n;
    
    cout << fact(n);
    
    return 0;
}

DFS

DFS(Depth First Search),又叫深度优先搜索,深度优先遍历,顾名思义,就是按照深度优先的顺序对“问题状态空间”进行搜索的算法,这一操作我们一般用递归来实现。

搜索树

每个递归问题都会产生一棵搜索树,如果我们刚开始对DFS不适应,可以多画画搜索树让自己加深对DFS的理解。

DFS例题

题目描述

给定一个整数n,将数字 1 ∼ n 1∼n 1n排成一排,将会有很多种排列方法。

现在,请你按照字典序将所有的排列方法输出。

输入格式

共一行,包含一个整数n

输出格式

按字典序输出所有排列方案,每个方案占一行。

数据范围

1 ≤ n ≤ 9 1≤n≤9 1n9

输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
样例解释+解题思路(回溯法)

n = 3 n = 3 n=3,需要按字典序输出 1 , 2 , 3 1,2,3 1,2,3三个数的全排列。
此时我们可以先画出搜索树。
搜索树
我们会发现,

  1. 每次需要从1到3搜索能用的数字,然后将其加入答案再往下递归。
  2. 我们需要传入一个能表示当前深度的参数u,当u == n时结束一轮递归
  3. 我们会发现一个数字需要重复使用,因此我们需要一个用来标记数字i是否用过的数组st
  4. 我们会发现从小到大枚举的情况下得到的全排列是天然按字典序排序的,因此不用额外考虑这个
  5. 我们会发现一个数字用完后在之后的全排列过程中中还会用到,因此我们还需要恢复现场,即将标记为用过的当前数字取消标记。
  6. 每一轮排列结束后我们都需要输出当前排列,因此我们还需要一个记录当前路径的数组path
代码
#include<iostream>
using namespace std;

const int N = 10;
int path[N]; // 记录全排列路径
int n;
bool st[N]; // 标记当前数字是否用过

void dfs(int u){ // 当前遍历到第u + 1位(u从0开始)
    if(u == n){ // 如果u已经从0 ~ n - 1这n个位置都遍历过
        for(int i = 0; i < n; i ++) cout << path[i] << ' '; // 输出当前排列
        cout << endl;
        return;
    }
    
    for(int i = 1; i <= n; i ++) // 从小到大枚举
        if(!st[i]){ // 如果当前数字没有用过
            st[i] = true; // 当前数字标记为用过
            path[u] = i; // 第u位数字填入数字i
            dfs(u + 1); // dfs下一位
            st[i] = false; // 恢复现场
        }
}

int main(){
    cin >> n;
    dfs(0);
    
    return 0;
}

注意:在恢复现场时本来应该将path[u] = 0,但是在之后path[u]会被覆盖,因此不用对它进行恢复现场。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

L_Hygen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值