top1编程
← 返回题目
题解

N个数的全排列

1 条题解

  • 0
    @ 2026-8-7 15:19:56

    P4917 N个数的全排列(入门)

    解题思路

    第一步,理解题意。 给出n个互不相同的正整数,它们已经按升序排好,我们要输出这n个数的所有全排列,并且每个数只能使用一次。输出顺序要优先输出较小的数字,也就是按字典序从小到大输出。例如输入7 9 12,第一行是7 9 12,最后一行是12 9 7。

    第二步,深度优先搜索。 由于输入已经升序,我们直接从小到大选数字即可。用一个数组ans存放当前排列,用used标记某个数是否已被选过。dfs(dep)表示已经确定了dep个位置:如果dep等于n,就把当前排列输出一行;否则,从下标0到n-1从小到大找第一个还没被用的数字放进第dep个位置,然后递归。

    第三步,回溯。 递归回来以后要把used标记还原,这样同一个数字可以被不同的位置使用。因为每个数互不相同,所以每个排列都不同,不会重复。

    第四步,和降序全排列对比。 另一道类似的题目是先排序再从大到小选,输出降序排列;这道题输入已经升序、从小到大选,输出升序排列。两者的搜索框架完全一样,只是枚举数字的顺序相反。

    第五步,验证样例。 输入3和7 9 12,从小到大选会依次输出7 9 12、7 12 9、9 7 12、9 12 7、12 7 9、12 9 7,正好是字典序从小到大,和样例一致。n最大9,排列总数9!约36万,完全可以在时限内输出。

    参考代码

    // N个数的全排列:输入已升序,从小到大选数构造排列,优先输出较小的数字
    #include <iostream>
    using namespace std;
    int a[12];
    int ans[12];
    int used[12];
    int n;
    void dfs(int dep) {
        if (dep == n) {
            for (int i = 0; i < n; i++) {
                if (i) cout << ' ';
                cout << ans[i];
            }
            cout << endl;
            return;
        }
        for (int i = 0; i < n; i++) { // 下标从小到大,先选小的数字
            if (used[i]) continue;
            used[i] = 1;
            ans[dep] = a[i];
            dfs(dep + 1);
            used[i] = 0;
        }
    }
    int main() {
        cin >> n;
        for (int i = 0; i < n; i++) cin >> a[i];
        dfs(0);
        return 0;
    }
    

    复杂度分析

    n个数一共有n!种排列,每种排列输出n个数,时间复杂度O(n·n!)。n最大9,9!约36万,即使每个排列都输出一遍也很快。空间上用了ans和used等几个数组,空间复杂度O(n)。

    • 1