码迷,mamicode.com
首页 > 其他好文 > 详细

Num 33 : 函数递归 [ 全排列 ]

时间:2015-08-12 11:32:58      阅读:117      评论:0      收藏:0      [点我收藏+]

标签:c语言   simple example   函数递归   全排列问题   



        数学上的全排列问题:

          给定m个数,可以排列成n位数的所有情况;


        例:3 个数 ( 1,2,3 ) 排列成两位数[ 含有重复数字 ]有:

          11,12 ,13,21,22,23,31,32,33;

        例:2个数( 1,2 ) 排列成三位数:

          111, 112, 121, 122, 211, 212, 221, 222;


       由上易找到规律


        对于 n 位 m 个要求的数 [ 不妨设为1,2,3的 2位数 ],我们只需从最高位开始,依次冠以第一个数( 11 );

        之后保持高位不变,最低位游历所有要排列的数:11, 12, 13 ;

        最低位游历过之后,次低位变为第二个数继续游历:21, 22, 23 ;

        知道最高位也游历过所有要排列的数为止;


       这样的问题通过for循环也是可以实现的,但是对于n位数,需要用n个for循环来游历所有位的数据,

       代码长度可想而知;

       所以我们想到了用递归的方法来解决;

 

        问题分析:


          1.

              因为我们不可能一次将排列的数按整形来输出,所以要把它的每一位存入数组中输出;

              需要定义两个数组 ( arr[ M ]和a[ N ] ) 分别代表了要排列的数和第 i 位[ i从0开始 ]的数;


          2.

              当 i 小于 n 的时候( 即没有达到n位数的要求的时候 ),游历第 i 位;

              当 i 大于等于 n 的时候( 已经满足要求 ) 按 i=0 到 i=n-1( 第一位到最后一位 )输出;


       实现代码( 含重复数字 ):

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
const int M=10;
int n,m,a[M],arr[M]={1 , 2 ,3 ,4};
void dfs(int v)
{
    if(v >= n)
	{
        for(int i = 0;i<n;i++)
            printf("%d ",a[i]);
        printf("\n");
        return ;
    }
    for(int i = 0; i<m;i++)
	{
        a[v] = arr[i];
        dfs(v+1);
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)==2)
	{
        dfs(0);
    }
	return 0;
}

       代码分析:


          1.

              dfs(0);  表示:从第一个( 第0个 )数开始排列 ;


          2.

              arr[ M ]={ 1,2,3,4 } 表示:要实现全排列的数有M个分别是 1, 2, 3, 4;


          3.

              当 i 小于 n 的时候( 即没有达到n位数的要求的时候 ),游历第 i 位;


for(int i = 0; i<m;i++)
	{
        a[v] = arr[i];
        dfs(v+1);
    }
      

          4.

              当 i 大于等于 n 的时候( 已经满足要求 ) 按 i=0 到 i=n-1( 第一位到最后一位 )输出;

  if(v >= n)
	{
        for(int i = 0;i<n;i++)
            printf("%d ",a[i]);
        printf("\n");
        return ;
    }


       以上的代码是实现含有重复数字的全排列,下面我们来看看不含有重复数字的全排列 :


          与含有重复数字的算法基本相同,唯一的不同在于 :


            不含有重复数字的全排列算法需要引入标记数组( mark[M] );


            注意, 当 m<n 的时候( 不重复排列的数小于位数 ),这种情况下是错误的,结果不予输出;


       实现代码( 含重复数字 ):

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
const int M=10;
int n,m,a[M],arr[M]={1,2,3,4},mark[M];
void dfs(int v)
{
    if(v >= n)
	{
        for(int i = 0;i<n;i++)
            printf("%d ",a[i]);
        printf("\n");
        return ;
    }
    for(int i = 0; i<m;i++)
	{
		if(!mark[i])
		{
			mark[i]=1;
			a[v] = arr[i];
        	dfs(v+1);
        	mark[i]=0;
		}
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)==2)
	{
		memset(mark,0,sizeof(mark));
        dfs(0);
    }
	return 0;
}

          1.

              利用回溯法:当 i 小于 n 的时候( 即没有达到n位数的要求的时候 ),游历第 i 位,并添加标记mark[i]=1;

              当mark[ i ]==1 的时候,说明已经使用过了,进行下一个数的排列;

              递归完毕后,返回,并将改为重新标记为 0;

    for(int i = 0; i<m;i++)
	{
		if(!mark[i])
		{
			mark[i]=1;
			a[v] = arr[i];
        	dfs(v+1);
        	mark[i]=0;
		}
    }

          2.

              当 i 大于等于 n 的时候( 已经满足要求 ) 按 i=0 到 i=n-1( 第一位到最后一位 )输出;

    if(v >= n)
	{
        for(int i = 0;i<n;i++)
            printf("%d ",a[i]);
        printf("\n");
        return ;
    }


       既然已经知道了,重复和不重复的全排列方法了,现在还有一个问题,就是 :

         如果不想输出由某个数字开头( 第i位 )的数字怎么办?( 可以在递归之前排除掉一些情况,节省时间 )


        例:3 个数 ( 1,2,3 ) 排列成两位数[ 含有重复数字 ][ 不输出由一开头的数字 ]:

          21,22,23,31,32,33;

        例:3 个数 ( 1,2,3 ) 排列成两位数[ 不含重复数字 ][ 不输出由一开头的数字 ]:

          21,23,31,32;


         实际上只需在刚开始加入数组的时候判断开头( 第 i 位 )是否为 k 即可;



①、实现代码如下[ 含有重复数字 ][ 不输出由一开头的数字 ]


#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
const int M=10;
int n,m,a[M],arr[M]={1 , 2 ,3 ,4};
void dfs(int v)
{
    if(v >= n)
	{
        for(int i = 0;i<n;i++)
            printf("%d ",a[i]);
        printf("\n");
        return ;
    }
    for(int i = 0; i<m;i++)
	{
        a[v] = arr[i];
        if(a[0]==1) continue;
        dfs(v+1);
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)==2)
	{
        dfs(0);
    }
	return 0;
}

   

   代码分析:

              仅仅加入了判断;


    for(int i = 0; i<m;i++)
	{
        a[v] = arr[i];
        if(a[0]==1) continue;
        dfs(v+1);
    }

            


②、实现代码如下[ 不含重复数字 ][ 不输出由一开头的数字 ]

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
const int M=10;
int n,m,a[M],arr[M]={1,2,3,4},mark[M];
void dfs(int v)
{
    if(v >= n)
	{
        for(int i = 0;i<n;i++)
            printf("%d ",a[i]);
        printf("\n");
        return ;
    }
    for(int i = 0; i<m;i++)
	{
		if(!mark[i])
		{
			mark[i]=1;
			a[v] = arr[i];
			if(a[0]==1)
			{
				mark[i]=0;
				continue;
			}
        	dfs(v+1);
        	mark[i]=0;
		}
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)==2)
	{
		memset(mark,0,sizeof(mark));
        dfs(0);
    }
	return 0;
}

 

  代码分析:

              在回溯法中加入了判断,需要在跳出之前,将标记取消( 否则所有含一的都不会输出 );


    for(int i = 0; i<m;i++)
	{
		if(!mark[i])
		{
			mark[i]=1;
			a[v] = arr[i];
			if(a[0]==1)
			{
				mark[i]=0;
				continue;
			}
        	dfs(v+1);
        	mark[i]=0;
		}
    }

      

         现在基本上可以对全排列随意操作了吧。 ≥~~≤;


版权声明:本文为博主原创文章,未经博主允许不得转载。

Num 33 : 函数递归 [ 全排列 ]

标签:c语言   simple example   函数递归   全排列问题   

原文地址:http://blog.csdn.net/helloworldonly/article/details/47438917

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!