C++经典算法 m个元素集合中提取n 个元素子集(有序排列)

前言

集合里有m个元素,从集合中取出n个元素任意的、有序的、不重复的组合,则这n个元素所组合成的子集有哪些

需求

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


{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}

 

10个组合。

这些子集已经使用字典顺序排列,如此才可以观察出一些规则: 如果最右一个元素小于m,则如同码表一样的不断加1

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

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

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

在实际写代码过程中,可以使用一个变数positon来记录加1的位置,position的初值设定为n-1, 因为我们要使用阵列,而最右边的索引值为最大的n-1,在position位置的值若小于m就不断加1,如果大于m了,position就减1,也就是往左移一个位置;

由于位置左移后,右边的元素会 经过调整,所以我们必须检查最右边的元素是否小于m,如果是,则position调整回n-1,如果不是,则positon维持不变。

 

代码示例

int _tmain(int argc, _TCHAR* argv[])
{
    int set[MAX];
    int m, n, position; 
    int i;
    int num=0; 

    printf("请输入集合个数 m:"); scanf("%d", &m);
    printf("请输入子集元素数 n:"); scanf("%d", &n);

    printf("有序排列组合共%d组", qsum(m,n));
    putchar('\n'); 
    printf("明细:");
    putchar('\n'); 

    for(i = 0; i < n; i++) 
        set[i] = i + 1;
    num++;
       
    printf("第%d组:", num);     // 显示第一组
    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;
        num++;
        printf("第%d组:", num);
        for(i = 0; i < n; i++) 
            printf("%d ", set[i]);
        putchar('\n');

        if(set[0] >= m - n + 1) 
            break;
    }
    putchar('\n');
    return 0;
   
}

 

运行结果:

 

 

感谢

本文参考网络相关文章,如有巧合,就是巧合了~

 

posted @ 2022-08-12 14:40  预见者  阅读(599)  评论(0编辑  收藏  举报