[递归] 组合1 | 从n个当中任选m个 | 在一个字符串中任选m个的全部可能 -C语言

版权声明:本文为博主原创文章,若有错误之处望大家批评指正!转载需附上原文链接,谢谢! https://blog.csdn.net/summer_dew/article/details/83111688

组合

【问题】从长度为n个字符串str中选出m个元素的可能

【思路】
在这里插入图片描述

【代码】

char *tmp;	//中间结果
int top;
int count;	//种数
//递归求组合数
void combination(char *str, int n, int m )
{
    if( n < m || m == 0 )    return ;		//case 1:不符合条件,返回
    combination( str+1, n-1, m );			//case 2:不包含当前元素的所有的组合
    tmp[ top++ ] = str[0];					//case 3:包含当前元素
    if( m == 1 ){								//case 3.1:截止到当前元素
        printA( tmp, top );
        printf("\n");
        count++;
        top--;
        return;
    }
    combination( str+1, n-1, m-1);				//case 3.2:包含当前元素但尚未截止
    top--;								//返回前恢复top值
}

【理解】将递归树进行先序输出
树状的方式类似于这种https://blog.csdn.net/summer_dew/article/details/82937941
在这里插入图片描述

【完整代码】

#include<stdio.h>
#include<stdlib.h>

char *tmp;	//中间结果
int top;	//
int count;	//种数

//打印长度为n的数组元素
void printA(char *str,int n)
{
    int i;
    for(i=0;i<n;i++){
        printf("%c ",str[i]);
    }
}

//递归求组合数
void combination(char *str, int n, int m )
{
    if( n < m || m == 0 )    return ;		//case 1:不符合条件,返回
    combination( str+1, n-1, m );			//case 2:不包含当前元素的所有的组合
    tmp[ top++ ] = str[0];					//case 3:包含当前元素
    if( m == 1 ){							//case 3.1:截止到当前元素
        printA( tmp, top );
        printf("\n");
        count++;
        top--;
        return;
    }
    combination( str+1, n-1, m-1);		//case 3.2:包含当前元素但尚未截止
    top--;										//返回前恢复top值
}

int main()
{

    int n,m;//存放数据的数组,及n和m
	char *str;
	printf("输入n与m,用空格隔开\n>>> ");
    scanf("%d%d",&n,&m);

    str = (char *) malloc( sizeof(char) * n );
    tmp = (char *) malloc( sizeof(char) * m );

	printf("输入字符串\n>>> ");
	scanf("%s", str);

	printf("\n%s %d中选取%d个", str, n, m);
    combination( str, n, m );//求数组中所有数的组合
	printf("总数%d\n", count);

    return 0;
}

错误思路

【举例】字符串’ABCD’,即4个元素中选3个出来
【思路】

有A的情况
	1. ABCD固定不动,取前三个位置--> ABC
	2. ABCD--> 将B、D互换--> ADCB--> 取前三个位置--> ADC
	3. ABCD--> 将C、D互换--> ABDC--> 取前三个位置--> ABD
没有A的情况:等同于,在字符串'BCD'中任选3个 => 回到了初始的问题
	1. BCD固定不动,取前三个位置--> BCD
	2. 没有其他元素了,就不必下去了

【转换成代码】

  1. 前面的思路其实是把字符串划分成了两个部分
    • “ABC”:当前序列
    • “D”:备选序列
  2. 实际就是把 ‘BC’ 中的每个元素,与’D’的每个元素,逐一交换后输出
    • “BC”:当前序列(“ABC”)中除去第一个元素’A’的序列
    • “D”:备选序列

【代码】

// 组合
void combination(char* str, int sbegin, int send)
{
	int i,j,z;
	
	// send没有超出元素的界限
	if ( send < n ) 
	{
		// -- 有str[sbegin]的时候
		// 原序列输出
		for ( z=0; z<m; z++ ) 
		{
			printf("%c", str[z+sbegin]);
			cnt++;
		}
		printf("\n");
		// 与备选的元素交换,找出其他可能
		for ( i=sbegin+1; i<=send; i++ ) { //目前序列除第一位的其他元素
			for ( j=send+1; j<n; j++ ) { //与备选序列中的每个元素
				swap(&str[i], &str[j]); //交换

				// 输出
				for ( z=0; z<m; z++ ) {
					printf("%c", str[z+sbegin]);
					cnt++;
				}
				printf("\n");

				swap(&str[i], &str[j]); //注意换回来
			}
		}
		
		// -- 没有str[sbegin]的时候
		combination(str, sbegin+1, send+1);
	}
}

【错误所在】
在这里插入图片描述
少了一种情况"ADE",–>"ABC"中"BC"和候选元素"DE"一起换的时候没有考虑到–> 里面不应该是简简单单的输出,也是一种递归

猜你喜欢

转载自blog.csdn.net/summer_dew/article/details/83111688
今日推荐