zoukankan      html  css  js  c++  java
  • Problem A: 【递归入门】全排列

    Description

         排列与组合是常用的数学方法。
    先给一个正整数 ( 1 < = n < = 10 )
    例如n=3,所有组合,并且按字典序输出:
    1 2 3
    1 3 2
    2 1 3
    2 3 1
    3 1 2
    3 2 1 

    Input

    输入一个整数n(  1<=n<=10)

    Output

    输出所有全排列

    每个全排列一行,相邻两个数用空格隔开(最后一个数后面没有空格)

    Sample Input Copy

    3
    

    Sample Output Copy

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

    参考链接:问题 A: 【递归入门】全排列

    最终AC代码:

    #include <cstdio>
    #include <cstring>
    const int MAX=15;
    int n, p[MAX];
    bool vis[MAX]={false};
    //这应该是一个经典的排列问题了 出现了很多次了!!! 
    void generateP(int index){
        if(index == n+1){ //表明已经已经有 n 个数了 
            for(int i=1; i<=n; i++){
                printf("%d", p[i]);
                printf(i<n?" ":"
    ");
            }
            return ;
        }
        for(int i=1; i<=n; i++){ //这里的逻辑要理解! 
            if(!vis[i]){
                p[index] = i; //这里不是 p[i]=x;!!! 
                vis[i] = true;
                generateP(index + 1); //生成后一个 
                vis[i] = false;
            }
        }
    }
    int main(){
        while(~scanf("%d", &n)) generateP(1);
        return 0;
    }

    总结:主要是对于生成的逻辑理解,即一下代码:

    for(int i=1; i<=n; i++){ //这里的逻辑要理解! 
        if(!vis[i]){
            p[index] = i; //这里不是 p[i]=x;!!! 
            vis[i] = true;
            generateP(index + 1); //生成后一个 
            vis[i] = false;
        }
    }

    我的理解(可能有不足)是:标号1~n共n个数的全排列,即要每次找到n个排列的数(每次还不能相同),并且将这些数输出。如何找呢?

    第一,一次全排列中,每个数只能出现一次,那么就用标记数组记录在当前排列趟数中,知道有哪些数已经使用了。

    第二,两趟排列中,只要任意两个位置数不同,那么就视为两种排列。换句话,就是标号为1~n的数字可以出现在任意位置上。

    第三,必须保证本趟排列与之前输出的排列不同。

    第二、三条件的限制比较难理解,也是我每次不知如何下手的根结所在。现在仔细想想,是不是可以这么理解:因为递归的性质,每一趟递归都可能在递归函数后面有很多未处理的逻辑,在本趟递归没完成前,我只专注地往更深层的递归走下去,直到达到递归边界。于是,可以将从递归开始到达到递归边界记录的排列,在达到递归边界时输出,然后再往上返回。

    但是,不是每次返回都从边界层返回到开始层。每回到上一层,别忘了此时可能还有其他未处理的代码,也就是前面递归遇到的未处理的逻辑。于是程序顺序执行,把当前层处理逻辑处理完,此时可能再次往更深层逻辑递归,而不是直接往上层返回(for循环可能多次满足条件,从而多次触发递归)。

    有意思的是在本程序代码中,本层深度的递归,其实就对应着p[]数组的第index个数据的生成。前面已经分析,任意一个数字可以出现在范围内的任意位置,换种理解,不就是任意位置可以是数字1~n中的某位(只要不重复出现,而变量i在循环增加,其实就保证了这个条件)。那么,可以在递归从深层返回到本层后,添加这个逻辑。

    再仔细想想,每次递归前标记,递归后解除标记,便保证了每一趟排列中不会出现两个一样的数(当然,希望以后如果遇到类似条件,能够想起这点),于是第三个条件也满足了。

    嗯,虽然只有几行代码,但是对于初学者来说,这逻辑真的比较难理解。此外,从理解这层逻辑到编写出正确、精简的代码,还是有很长一段路程的。所以,好好刷题,熟能生巧嘛~加油!

  • 相关阅读:
    教你发布Silverlight Bussiness Application(SQL Server 登录,局域网访问,以及使用ArcGIS Server服务需要注意的问题)
    ArcGIS API for Silverlight 使用GeometryService进行河流网格划分(三)
    ArcGIS Server 10.1发布要素服务时遇到的数据库注册问题总结 (二)
    使用ArcGIS API for Silverlight + Visifire绘制地图统计图
    使用Visifire+ArcGIS API for Silverlight实现Graphic信息的动态图表显示
    报个道
    Cgroups
    docker的文件系统
    go实现http服务
    linux调度器原理
  • 原文地址:https://www.cnblogs.com/heyour/p/12639045.html
Copyright © 2011-2022 走看看