1. 程式人生 > >[遞迴] 組合 | 從n個當中任選m個 | 在一個字串中任選m個的全部可能 -C語言

[遞迴] 組合 | 從n個當中任選m個 | 在一個字串中任選m個的全部可能 -C語言

組合

【問題】從長度為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. 沒有其他元素了,就不必下去了

【轉換成程式碼】

  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"一起換的時候沒有考慮到–> 裡面不應該是簡簡單單的輸出,也是一種遞迴