C/C++递归实现组合数

324 阅读3分钟

前言

本文将介绍如何使用递归算法实现组合数。


组合数

在这里插入图片描述 参考题目:递归实现组合数

组合数,即Cnm,在n个数中选取m个数,不重复,一共有几种选法。

以C53为例,在1、2、3、4、5五个数中选择三个,将所有选法打印。

即一共有五个位置,每个位置都提供给我们两个选择:选,或不选。

因此,我们创建一个数组arr【25】用来保存每个位置的选择情况。

	static arr[25] = { 0 };//在函数内部创建,需要放入静态区,也可设为全局变量

如果第一个位置选了,就把arr【0】设为1;如果没选,就将其设为0;

首先,我们写出主函数。

int main()
{
	int n = 0;
	int m = 0;

	scanf("%d %d", &n,&m);
	C(n,m);//即在n个数中选择m个数
	return 0;
}

当我们进入C(n,m)时,首先要选择的是第一个位置,即arr中下标为0的数,而题目中要求按照字典序输出,所以我们不能以n作为当前的选择位置。

因此我们再在静态区设置一个变量step,用来存储当前我们正在选择第几个位置,当前代码如下:

void C(int n,int m)
{
	static arr[25] = { 0 };//在函数内部创建,需要放入静态区
	static step = 0;
}

当我们第一次进入C(n,m)后,此时step==0,即是对第一个位置选择,有两种情况,分别是选或不选。

如果选择第一个位置,那么arr【step】应该设为1,表明第一个位置我们选了。选了第一个位置以后,开始选第二个位置,所以要把step++。

既然第一个位置被选了,因为我们一共选择m个,所以进入下一个位置的选择时,参数m的位置要传入m-1才行。

此时是第一种情况,当第一种情况的所有分支都进行完以后,就要开始第二种情况的分支,但是此时arr【0】已经是1了,所以我们要把step退回至0,然后让arr【step】再设为0,表示第一个位置我们没选,才好进行没选情况下的分支,像这样:

void C(int n,int m)
{
	static arr[25] = { 0 };//在函数内部创建,需要放入静态区
	static step = 0;
	//选
	arr[step]++;
	step++;
	C(n,m-1);
	step--;
	arr[step]--;

	//不选
	step++;
	C(n,m);
	step--;
	return;
}

观察此代码中间部分可以发现,以下三条语句可以合并为一条. 在这里插入图片描述 合并后:arr[step-1]--;

那么,什么时候此递归才会停止呢?

因为本题中一共有五个位置,所以当step等于0、1、2、3、4时都是在进行选择,当step等于5时,就是代表所有位置我们都已经检测一遍了,可以进行判断是否要打印了。

那么打印的条件又是什么呢?因为我们只能选m个,且必须选m个。每选了一个位置,m就要-1,当m刚好等于0时,就代表着刚好选了三个位置,此时就可以打印了,像这样:

void C(int n,int m)
{
	static arr[25] = { 0 };//在函数内部创建,需要放入静态区
	static step = 0;
	if (step == n)//五个位置都判断了
	{
		if (m == 0)//刚好选了m个才打印
		{
			int i = 0;
			for (i = 0; i < n; i++)
			{
				if (arr[i] == 1)
				{
					printf("%d ", i+1);
				}
			}
			puts("");
		}
		return;
	}
	//选
	···
	//不选
	···
}

在本题中,因这五个数刚好是1、2、3、4、5,所有博主可以像上面那样打印出来。

如果是五个毫不相干的数,那么你可以再创建一个数组保存这五个数,利用下标来打印。

比如说:27,38,44,89,63。这五个数,那么你可以这样:

int data[5]={27,38,44,89,63};
for(int i=0;i<n;i++)
{
	if(arr[i]==1)
	{
		printf("%d ",data[i]);
	}
}
	

如此,就可以做到在n个随机数中选取m个的数的递归实现。

感谢您的阅读与耐心~