每日一算法:m元素集合的n个元素子集

说明

假设有个集合拥有m个元素,任意的从集合中取出n个元素,则这n个元素所形成的可能子集有那些?

解法

假设有5个元素的集点,取出3个元素的可能子集如下:

{1 2 3}、{12 4 }、{12 5}、{13 4}、{13 5}、{14 5}、{23 4}、{23 5}、{24 5}、{34 5}

这些子集已经使用字典顺序排列,如此才可以观察出一些规则:

如果最右一个元素小于m,则如同码表一样的不断加1

如果右边一位已至最大值,则加1的位置往左移

每次加1的位置往左移后,必须重新调整右边的元素为递减顺序

所以关键点就在于哪一个位置必须进行加1的动作,到底是最右一个位置要加1?还是其它的位置?

在实际撰写程式时,可以使用一个变数positon来记录加1的位置,position的初值设定为n-1,因为我们要使用阵列,而最右边的索引值为最大 的n-1,在position位置的值若小于m就不断加1,如果大于m了,position就减1,也就是往左移一个位置;由于位置左移后,右边的元素会 经过调整,所以我们必须检查最右边的元素是否小于m,如果是,则position调整回n-1,如果不是,则positon维持不变。

#include <stdio.h>

#define MAX  20

int main()
{
	int set[MAX];
	int m,n,position;
	int i;
	printf("输入集合个数:");
	scanf("%d",&m);
	printf("输入取出元素个数:");
	scanf("%d",&n);
	for (i=0; i<n; i++)
	{//设置第一个集合
		set[i] = i+1;
	}
	for (i=0; i<n; i++)
	{//打印第一个集合
		printf("%d ",set[i]);
	}
	printf("
");
	position = n-1;//设置当前位置指向第一个集合的最后一个元素
	while (1)
	{
		if (set[n-1] == m)
		{//如果最后一个元素已经是最大元素,则位置前移,指向前一个元素
			position--;
		}
		else
		{//否则位置指向最后一个元素
			position = n-1;
		}
		set[position]++;//当前位置值加1
		for (i=position+1; i<n; i++)
		{//后面的值逐个处理,即比前一个元素大1
			set[i] = set[i-1]+1;
		}
		for (i=0; i<n; i++)
		{//显示集合
			printf("%d ",set[i]);
		}
		printf("
");
		if (set[0] >= m-n+1)
		{//已经处理完毕
			break;
		}
	}
	return 0;
}


经验分享 程序员 微信小程序 职场和发展