[遞迴] 組合 | 從n個當中任選m個 | 在一個字串中任選m個的全部可能 -C語言
阿新 • • 發佈:2018-12-15
組合
【問題】從長度為n個字串str中選出m個元素的可能
//遞迴求組合數
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值
}
【完整程式碼】
#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. 沒有其他元素了,就不必下去了
【轉換成程式碼】
- 前面的思路其實是把字串劃分成了兩個部分
- “ABC”:當前序列
- “D”:備選序列
- 實際就是把 ‘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"一起換的時候沒有考慮到–> 裡面不應該是簡簡單單的輸出,也是一種遞迴