组合数打印

简介: 个人推荐使用递归数列打印组合。

[北京直真笔试题]比如给定4个数,分别为1,2,3,4。现在要求从中选取3个的组合数,不能重复。

即打印:123,124,234...。

方法1:【思路】1)将1,2,3,4存入数组中,然后从4个数中选出1个数,即为selVal;2)接下来的工作即是从剩余的3个数中选取2个数,需要存储除selVal外的剩余3个数;3)选取后打印selVal和选的2个数即可。

【分析】:时间复杂度O(n3),空间复杂度O(n)。

const int g_nCnt = 4;
int g_nArr[g_nCnt] ={1,2,3,4};
//从3个里面选2个数的排列方式.
void selTwoFromThree(intnArray[], int nSize, int nAlreadySel)
{
for(int i = 0; i < nSize; i++)
   {
           for(int j = i+1; j < nSize; j++)
           {
                    cout << nAlreadySel << "\t"<< nArray[i] << "\t" << nArray[j] << endl; //先i后j
                    cout << nAlreadySel << "\t"<< nArray[j] << "\t" << nArray[i] << endl; //先j后i
           }
  }
}
 
void printArray(intnArray[], int nSize)
{
int nAleardySel = 0;
for(int i = 0; i < nSize; i++)
{
           int *pArrAdd = new int[3];
           int k = 0;
           nAleardySel = nArray[i];
           for(int j = 0; j < nSize; j++)
           {
                    if(nArray[j] != nAleardySel)
                    {
                             pArrAdd[k++] = nArray[j];
                    }//end if
           }//end for j
          
           selTwoFromThree(pArrAdd,g_nCnt-1,nAleardySel);
           cout << endl;
}//end fori
}

方法2:【思路】 将4个数中选择的3个数看做百位数,因此就变成了打印的过程,变成了从4个数中选3个数组成的全排列444=64个中选择百位、十位、个位不重复的432=24个数的过程。

【分析】:时间复杂度O(n3),空间复杂度O(1)。不需要额外的开辟空间。

#define MAXN 4
void combineSelPrint(intmaxCnt)
{
static int count = 0;
for(int i=1;i<=MAXN;i++)//百位的情况
{
           for(int j=1;j<=MAXN;j++)//十位的情况
           {
                    if(j != i)
                    {
                             for(int k=1;k<=MAXN;k++)//个位的情况
                             {
                                       if(k != j && k != i)
                                       {
                                                printf("%d,%d,%d\n",i,j,k);
                                                ++count;
                                       }
                             }//end for k
                    }//end if j
           }//end for j
}//end for i
cout << "--------------count = " << count<< "-------------" << endl;
}
 
 
int main()
{
combineSelPrint(MAXN);
return 0;
}

【思考?】:有没有时间复杂度低的算法?或者递归实现的好方法?

【方法3】:递归实现组合数打印,C(n,m),从n个数中选出m个数(m<=n)个的全部组合打印。

有很多文章讨论如何打印全排列的,毕竟它是很多遍历问题的基础嘛。这里介绍的是如何打印组合数。
先看一个例子:

C(5,3) = 10
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 | 2 3
1 | 2 4
1 | 2 5
1 | 3 4
1 | 3 5
1 | 4 5 
------ C(4, 2)∵可以在{2, 3, 4, 5}中挑2个出来。
2 | 3 4
2 | 3 5
2 | 4 5 
------ C(3, 2)∵可以在{3, 4, 5}中挑2个出来。
3 | 4 5 
------ C(2, 2)∵只能在{4, 5}中挑2个出来。

 

这样就很容易写出递归算法来。

Algorithm combination(n, k, A[l..n+l-1])
1. if k = 0
2. print ary[1..k]
3. else 
4. for i←1 to n-k+1
5. ary[index++] = A[l+i-1]
6. combination(n-i,k-1, A[l+i..n+l-1])
7. --index
8. endfor

大家可能会疑惑干嘛要弄出个index,还有一加一减的(你手工算一下就知道了)。

 

【实现部分】

int *dst_array,top=0;//中间数组,存放中间求解过程,count计数所有的组合个数
int cnt = 0;
 
//打印长度为n的数组元素
static void printA(int*parray,int n)
{
    int i;
    for(i=0;i<n;i++)
    {
        printf("%d ",parray[i]);
    }
}
 
//递归打印组合数
static  void print_combine(int *pArray,int n,int m)
{
    if(n < m || m==0)  
    {
           return ;//情况一:不符合条件,返回
    }
 
    print_combine(pArray+1,n-1,m);//情况二:不包含当前元素的所有的组合
 
    dst_array[top++]=pArray[0];//情况三:包含当前元素
    if(m==1)
     {  //情况三-1:截止到当前元素
        printA(dst_array,top);
        printf("\n");
        cnt++;
        top--;
        return;
    }
 
    print_combine(pArray+1,n-1,m-1);//情况三-2:包含当前元素但尚未截止
    top--;//返回前恢复top值
}
 
int main()
{
    int n,m,*parray;//存放数据的数组,及n和m
    printf("---以下实现从n个数中选出m个数的全组合打印(n个数为1,2,3....n---\n");
    printf("---请输入n 和m \n---");
    scanf("%d%d",&n,&m);
 
    printf("\n---以下是输出结果---\n");
    parray=(int *)malloc(sizeof(int)*n);
    dst_array=(int *)malloc(sizeof(int)*m);
 
    int i;
    for(i=0;i<n;i++)
    {
           //初始化数组
        //scanf("%d",&parray[i]);
           parray[i] = i+1;
    }
    print_combine(parray,n,m);//求数组中所有数的组合
 
    printf("=====C(%d,%d)共计:%d个=====",n,m,cnt);
 
    free(parray);
    free(dst_array);
    return 0;
}

方法三参考:http://bbs.pfan.cn/post-270256.html

http://blog.csdn.net/challenge_c_plusplus/article/details/6641950

笔者调试了方法三,好用。但是笔者对其中递归的部分的原理甚是不解,有些疑惑,望大家介绍下。

相关文章
|
8天前
|
云安全 人工智能 算法
以“AI对抗AI”,阿里云验证码进入2.0时代
三层立体防护,用大模型打赢人机攻防战
1400 10
|
8天前
|
机器学习/深度学习 安全 API
MAI-UI 开源:通用 GUI 智能体基座登顶 SOTA!
MAI-UI是通义实验室推出的全尺寸GUI智能体基座模型,原生集成用户交互、MCP工具调用与端云协同能力。支持跨App操作、模糊语义理解与主动提问澄清,通过大规模在线强化学习实现复杂任务自动化,在出行、办公等高频场景中表现卓越,已登顶ScreenSpot-Pro、MobileWorld等多项SOTA评测。
1257 5
|
9天前
|
人工智能 Rust 运维
这个神器让你白嫖ClaudeOpus 4.5,Gemini 3!还能接Claude Code等任意平台
加我进AI讨论学习群,公众号右下角“联系方式”文末有老金的 开源知识库地址·全免费
1108 14
|
3天前
|
人工智能 前端开发 API
Google发布50页AI Agent白皮书,老金帮你提炼10个核心要点
老金分享Google最新AI Agent指南:让AI从“动嘴”到“动手”。Agent=大脑(模型)+手(工具)+协调系统,可自主完成任务。通过ReAct模式、多Agent协作与RAG等技术,实现真正自动化。入门推荐LangChain,文末附开源知识库链接。
396 118
|
6天前
|
存储 缓存 NoSQL
阿里云经济型e实例(ecs.e-c1m4.large)2核8G云服务器优惠活动价格及性能测评
阿里云经济型e实例(ecs.e-c1m4.large)2核8G配置,支持按使用流量或按固定带宽两种公网计费方式,搭配20G起ESSD Entry云盘,是主打高性价比的内存优化型入门选择。其核心特点是8G大内存适配轻量内存密集场景,计费模式灵活可控,既能满足个人开发者的复杂测试项目需求,也能支撑小微企业的基础业务运行,无需为闲置资源过度付费。以下从优惠活动价格、性能表现、适用场景及避坑要点四方面,用通俗语言详细解析。
224 153
|
3天前
|
机器学习/深度学习 人工智能 算法
炎鹊「Nexus Agent V1.0」:垂直领域AI应用的原生能力引擎
炎鹊AI「Nexus Agent V1.0」是垂直行业专属AI原生引擎,融合大模型、AIGA决策大脑、行业知识图谱与专属模型,打造“感知-决策-执行”闭环。支持21个行业低代码构建工具型、员工型、决策型AI应用,实现技术到业务价值的高效转化,推动AI从实验走向规模化落地。(239字)
242 1

热门文章

最新文章