DFS和递归不得不说的秘密

DFS

就我目前学习过的知识来说,dfs其实就是树的前序遍历!就是递归!

image-20220806171431845

回顾一下前序遍历:

  1. 从根节点(A)开始,首先一直遍历左子树
  2. 当遍历完左子树时在结点D,此时打印,并且回溯到之前的节点,开始遍历右子树
  3. 在当前节点查找是否有右子树,如果有则进入右子树,反之继续回溯

贴上代码

void preOrder(Node root){
    if(root == NULL) return;   //如果走到NULL了,那就表示已经到头了,直接返回
    printf("%c", root->element);
    preOrder(root->left);
    preOrder(root->right);
}

很神奇是吧,短短四句话就可以概括整个过程,也让多少人受苦,让我们调试看看他是怎么进行的:

  1. 在$dfs(A)$函数中,进行到$dfs(B)$时停止$dfs(A)$的运行,进入$dfs(B)$
  2. $dfs(B)$继续进行,到$dfs(D)$里,此时嵌套了三个函数dfs(A),dfs(B),dfs(C)
  3. 重点开始了!,当我们在$dfs(D)$函数里进行dfs(root->left)时,发现root->left其实是NULL的!这意味着要开始回溯了!
  4. 因此在$dfs(B)$函数里preOrder(root->left)运行结束,开始运行 preOrder(root->right);
  5. 在(4).的基础上$dfs(B)$函数运行结束,也就是意味着$dfs(A)$函数里的preOrder(root->left)运行结束,开始运行A函数里的preOrder(root->right),即开始走C

具体右边的分析也是同理,这就是递归的功劳。第一次写这样的函数肯定会觉得有点抽象和难以理解,但见多了之后就可以找到一点感觉了。

那这样子我们就直接上dfs的例题吧!

洛谷B3621-枚举元组

#include <bits/stdc++.h>
using namespace std;
int st[20];
int n,k;
void dfs(int x);
int main() {
    cin>>n>>k;
    dfs(1);
    return 0;
}

void dfs(int x){
    if(x>n){
        for(int i=1;i<x;i++){
            cout<<st[i]<<" ";
        }
        cout<<endl;
        return;
    }

    for(int i=1;i<=k;i++){
        st[x]=i;
        dfs(x+1);
    }
}    

其实这题也是可以用暴力枚举做的,不过不优雅

分析过程:

  1. 先对第一个位置的元素判断,有k种状态(1~k),但我们从1开始。判断是否能去下一个位置
  2. 在当前状态下看第二个位置,依旧也是k种状态,我们还是从1开始,讲究顺序。
  3. 一直到最后一个位置填上1,判断出是否能去下一个位置
  4. 所以为了判断能否进行下一步,我们要设置临界条件

我们要输出n个,意味着我们dfs函数的参数x不能超过n,当超过n的时候,那意思就是越界了,已经走完能走的所有位置,于是开始打印元素。

总结

其实这玩意就是递归,然而递归的感觉也只能在多做题里找到

总之,远离OI,人人有责

posted @ 2024-03-09 22:53  AntoiDo  阅读(37)  评论(0)    收藏  举报