数组a中有n个元素,从其中选出m个元素,输出这m个元素所有不同的组合

题目:数组a中有n个元素,从其中选出m(m < n)个元素,输出这m个元素所有不同的组合

分析:

举例如:1 2 3 4 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

观察上述例子中,选择的步骤是这样的:

  1. 从{1, 2, 3, 4, 5}中任意选出第1个元素,如选出了1,
  2. 然后再从{2, 3, 4, 5}中选择第2个元素,选出2,
  3. 那么再从{3, 4, 5}中选出第3个元素,假设为3,
  4. 没有要选择的元素了,最终形成一个组合<1,2,3>。

仔细观察,可以发现,上述过程是递归进行的:

  1. 检查m是否大于等于1,若大于等于1,进行步骤2;否则执行步骤3;

  2. 从数组 a[i] ~ a[n-1] 中选出1个元素,假设被选元素在数组中的下标是j (i <= j <= n - 1),那么接下来应该从下标为 j ~ n-1 中选取出 m-1 个,我们令 m = m - 1;再次执行步骤1;

  3. m 若等于0,说明 m 个元素已经已经选择出来了,此时将这些被选出来的元素输出即可。

为了引入递归函数combine,减少不必要的传参,我们可以将上述过程进行稍加改动:

每次选择时在数组a中从后向前选择,而不是从前向后,即每次选择都是从 a[n-1] ~ a[0] 倒序依次选择一个元素,假设选出的元素下标为i,引入数组sel记录每次选择的元素下标i,即 sel[m-1] = i; 然后令 n = i; 再次执行选择,直到 m = 0,进行回溯,此时进行一趟选择出m个元素的下标都已经被记录在sel数组中了,只需要将其逆序打印出来即可。

回溯条件:

if(m == 0){
    //此处将sel数组记录的被选元素打印出来,或另做处理
    return;
}

递归条件: m > 0

for(i = n-1; i >= 0; i--){
    sel[m - 1] = i;
    //...此处执行递归选择
}

完整递归代码如下:

#include <stdio.h>

/**
    C(n,m) 组合--递归实现
    
参数:
    arr -- 元素数组指针
    n   --  数组大小
    m   -- 选择的个数,递归过程会变
    sel --  记录被选元素下标的数组指针
    M   --  m,初始选择元素的个数,固定不变
**/
void combine(int *arr, int n, int m, int *sel, const int M)
{
    int i;
    if(m == 0)
    {
        for(i = M - 1; i >= 0; i--)
        {
            printf("%d ", arr[sel[i]]);
        }
        printf("\n");
        return ;
    }
    /*从最后 arr[n-1] ~ a[m-1] 之间依次选取一个数;为什么是到arr[m-1]?
    因为arr[0] ~ arr[m-1]不够m个数了。
    */
    for(i = n - 1; i >= m-1; i--)
    {
        sel[m - 1] = i;   //选择当前位置作为第m个选择的数
        combine(arr, i, m - 1, sel, M); //递归选择前m-1个数
    }
}
int main()
{
    int i,n, m, a[MAX_SIZE], s[MAX_SIZE];
    scanf("%d", &n);
    scanf("%d", &m);
    for(i=0; i<n; i++)
    {
        scanf("%d", a + i);
    }
    combine(a, n, m, s, m);
    return 0;
}

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
这个问题可以用回溯法来解决。回溯法是一种通过穷举所有可能的候选解来找出所有的解的算法。具体来说,在回溯法中,我们首先选择一个候选解,然后递归地搜索剩余的候选解,直到找到所有的解或者确定不存在解为止。 下面是一个使用回溯法来解决这个问题的 Python 代码实现: ```python def combinations(n, m): def backtrack(start, path): if len(path) == m: res.append(path[:]) return for i in range(start, n+1): path.append(i) backtrack(i+1, path) path.pop() res = [] backtrack(1, []) return res ``` 这个函数的输入是两个整数 `n` 和 `m`,表示在 1 到 `n` 之间选出 `m` 个数字组合。函数的输出是一个列表,包含所有可能的组合方式。函数中使用了一个内部函数 `backtrack`,它负责递归地搜索候选解。在搜索过程中,我们维护了一个 `path` 列表,它存储了当前已选的数字。如果 `path` 的长度等于 `m`,那么就将 `path` 添加到结果列表 `res` 中。否则,我们从 `start` 开始枚举所有可能的数字,并将其添加到 `path` 中,然后递归地搜索剩余的数字。搜索完成后,需要将 `path` 中最后添加的数字弹出,以便进行下一次搜索。 下面是一个例子,演示了如何使用 `combinations` 函数来输出所有可能的组合方式: ```python >>> combinations(4, 2) [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] >>> combinations(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]] ``` 在这个例子中,`combinations(4, 2)` 输出了 1 到 4 中选出 2 个数字的所有组合方式,`combinations(5, 3)` 输出了 1 到 5 中选出 3 个数字的所有组合方式。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT研究僧大师兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值