如何使用回溯打印所有排列?

Posted

技术标签:

【中文标题】如何使用回溯打印所有排列?【英文标题】:How to print all permutation using backtracking? 【发布时间】:2021-03-25 12:19:56 【问题描述】:

此代码打印 N-1 个项目的所有排列。但我无法理解一件事: 当 n=N 时,它返回调用它的位置并使 flag[n-1] = false。因此,i = N-1 并打破循环。但是当 n=N-2 到 0 时,其余的排列如何打印或返回?

void perm(int n) 
    if (n == N) 
        for (int i = 0; i < N ; i++) 
            cout<<a[i]<<" ";
        
        cout<<endl;
        return;
    
    for (int i = 0; i < N; i++) 
        if (flag[i]) continue;
        a[n] = i;
        flag[i] = true;
        cout<<i<<endl;
        perm(n + 1);
        cout<<i<<endl;
        flag[i] = false;

    

【问题讨论】:

这是一个使用调试器的好机会 您能解释一下您的代码中的Nflaga 是什么吗? N 是排列长度,flag 是给定数字是否已被使用,a 是给定位置的选定数字。 您的问题是 “” 问题中的代码是 “打印 N-1 项的所有排列”。 你的实际问题是什么? 【参考方案1】:

您需要考虑到函数调用最终是嵌套的。

下面的每个缩进显示一个嵌套调用:

main()
    entering perm(0)
        entering perm(1)
            i = 0
            entering perm(2)
                let's say N is 2, this will print and return

        now perm(1) continues
            i becomes 1
            entering perm(2)
...
        

由于return 仅从当前函数调用返回,而不是所有函数调用,排列继续打印。

为了熟悉这个,试试这个:

void perm(int n) 
std::cout << "Entering perm " << n << std::endl;
if (n == N) 
    for (int i = 0; i < N ; i++) 
        cout<<a[i]<<" ";
    
    cout<<endl;
    std::cout << "Exiting perm " << n << std::endl;
    return;

for (int i = 0; i < N; i++) 
    if (flag[i]) continue;
    a[n] = i;
    flag[i] = true;
    cout<<i<<endl;
    std::cout << "about to call perm" << std::endl;
    perm(n + 1);
    std::cout << "finished call to perm" << std::endl;
    cout<<i<<endl;
    flag[i] = false;


    std::cout << "Exiting perm " << n << " (2)"<< std::endl;

【讨论】:

当调用 perm(2) ,i=1 时,它返回并使 flag[1] = false。因此,它打破了循环,并且没有其他返回关键字。但是 perm(1) 如何继续?打破循环后,它会自行进入 perm(1) 吗?我错过了吗? 是的,在调试器中试试。您似乎还不熟悉嵌套函数调用。请记住,如果perm 调用自身,它将在返回时继续原处。 i 也会有多个值,每个嵌套级别一个 非常感谢!

以上是关于如何使用回溯打印所有排列?的主要内容,如果未能解决你的问题,请参考以下文章

使用回溯创建给定列表的排列列表:我做错了啥?

C++ 回溯排列

当我告诉它时,如何使用 Django 的记录器来记录回溯?

剑指 Offer 38. 字符串的排列-全排列-回溯

剑指 Offer 38. 字符串的排列(dfs回溯实现全排列,Java)

字符串的排列