0003 || 递归实现排列型枚举 (DFS深度优先搜索)

166 阅读3分钟

0003 || 递归实现排列型枚举 (DFS深度优先搜索)

题目

把 1 ∼ n 这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。

输入格式

一个整数 n。

输出格式

按照从小到大的顺序输出所有方案,每行 11 个。

首先,同一行相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。

数据范围

1 ≤ n ≤ 9

输入样例:

3

输出样例:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

以下是代码的实现

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 10; // 起点是 1, 那么终点是9的话,数组需要加一位。
int st[N];        // 声明的全局变量在C++中,默认全部赋值为0.
int n;
bool use[N];      // 用来声明当前数的状态是用过还是没用过,用过true, 没用过false。

void dfs(int u)
{
    if(u > n)     //用来判断枚举完了最后一位,用来最后的输出。
    {
    for(int i = 1; i <= n; i++)
    {
        printf("%d ", st[i]);    //st[i]用来存放当前值。 
    }
    printf("\n");
    return;
    }
    
    for(int i = 1; i <= n; i++)
    {
        if(!use[i])             //用来判断当前位数是否存在值,不存在数,
        {                       //进行枚举之后接着进入下一个递归,进行枚枚举。
            st[u] = i;
            use[i] = true;
            dfs(u + 1);
            st[u] = 0;          //恢复现场
            use[i] = false;
        }
    }
}


int main()
{
    scanf("%d", &n);
    
    dfs(1);
    
    return 0;
}

思想总结

对于这个排列型枚举, 在我看来是先实现对于递归树的实现。对于这个题目中没个位数出现的可能性用树列出来。

其次,对树进行代码实现,可以利用递归的方法从最后一层进行实现, 即输出。

我们还需要对每一层的状态进行判断,所以我们用了 use[N] 这样的数组来进行实现, 我们对于每个位数的使用状态进行判断从而赋值再递归。

最后, 我们需要对赋值后递归下一层返回当层进行赋值,即恢复现场,我们不能对赋值递归后的当前层数进行破坏后不回复,这样子就会进行死循环。

特别的,在主函数外进行定义的时候,我们的定义并不需要初始化,因为已经直接赋值为 0 了。是因为局部变量是在栈下执行的,而栈是用来执行指令的。全局变量是在堆上执行的,是用来存数据的,标记为 0 了。

我认为在DFS中很有意思的事情就是,我们很需要一个数组来记录每一层递归下的状态,随后再递归到下一层,之后我们需要对当前层恢复现场,就像原本没发生赋值一样,而在记录当前层状态时,同时的,我们需要对当前层进行操作赋值(即对当前位数进行操作),使得他在下一层递归不会因为上一层没操作(即上一位)而导致我们下一层无限循环最后time limit。以上就是我认为DFS深度优化搜索的特点。