问题引入
假设有一个集合U,在集合U中共含有m个元素。如果要从这m个元素中,每次取出n个元素组成只含有n个元素的子集,应该如何实现???
问题分析
假设集合U中含有5个元素,即U={1,2,3,4,5}。
现在从U中每次取n=3个元素组成集合U的子集,则可能的取值情况如下所示:
{1,2,3}、{1,2,4}、{1,2,5}、{1,3,4}、{1,3,5}、{1,4,5}、{2,3,4}、{2,3,5}、{2,4,5}、{3,4,5},
这些子集中的元素已经经过排序,那么如何实现取数同时又要确保数字为3个并不断右移?通过上述例子不难发现:在取数过程中,如果集合中最右边一个元素小于m,为了实现取数,类同于码表一样不断加1;如果右边一位已是最大值,则加1的位置往左移。每次加1的位置往左移后,必须重新调整右边的元素顺序。问题的关键就在于在哪个位置进行加1。
为更高效的编写程序,定义一个变量positon记录加1的位置,position的初值设定为n-1, 因为要使用数组,而最右边的索引值为最大的n-1(数组长度)。(例如最初5个元素中取3个元素组成子集,之后不看1,从2、3、4、5中取3个元素组成子集,那么m相当于为4)。因此,在position位置的值小于m就加1,也即往右移一个位置;如果大于m就减1,也即往左移一个位置。由于位置左移后,右边的元素会经过调整,所以我们必须检查最右边的元素是否小于m,如果是,则position调整回n-1,如果不是,则positon维持不变。
代码实现
#include <stdio.h>
#include <stdlib.h>
#define MAX 20 //设置子集数判断数组容量,可根据需求修改
int main(){
int set[MAX];
int m, n, position;
int i;
printf("请输入集合U中含有的元素个数 m:");
scanf("%d", &m);
printf("请输入从集合U取出几个元素组成集合U的子集 n:");
scanf("%d", &n);
for(i = 0; i < n; i++){
set[i] = i + 1;
}
// 显示第一个集合
for(i = 0; i < n; i++){
printf("%d ", set[i]);
}
putchar('\n');
position = n - 1;
while(1){
if(set[n-1] == m){
position--;
}else{
position = n - 1;
}
set[position]++;
// 调整右边元素
for(i = position + 1; i < n; i++){
set[i] = set[i-1] + 1;
}
for(i = 0; i < n; i++){
printf("%d ", set[i]);
}
putchar('\n');
if(set[0] >= m - n + 1){
break;
}
}
return 0;
}
运行结果