1. 程式人生 > >簡單列舉---從一陣列中任取n個元素

簡單列舉---從一陣列中任取n個元素

在解決POJ753-Flip Games之前,我們先來看這樣一個問題:"給出一個數組a[6]={1,2,3,4,5,6},求從中任取n個元素的所有組合。"

如果任取1個元素,那麼組合是:

1  ,2  ,3  ,4  ,5 ,6.

如果任取2個元素,那麼組合是:

1 2,1 3,1 4,1 5,1 6.

2 3,2 4,2 5,2 6.

3 4,3 5,3 6.

4 5,4 6.
5 6.

不再舉例。

這個問題很明顯的需要用列舉的思想來一一遍歷陣列元素並進行無重複組合,但是演算法該怎麼實現呢?

如果任取3個元素,首先我得先找第一個元素,考慮到不重不漏:


第一個元素只能從1~4中選取;
假如第一個元素取1,第二個元素就只能從2~5中選取;
如果第一個元素選1第二個元素選2,第三個元素就只能從3~6中選取。
以此類推。

所以這時我們就可以考慮到用遞迴的思想來解決列舉的演算法:

首先列舉選取第一個元素,並把其儲存在一個數組中,在第一個元素列舉完後,如果規定的元素選取個數沒有達到,則從已取得的第一個元素之後開始,再進行列舉,直到完成規定元素選取個數。在列舉選取的同時把選出來的元素按序依次儲存在一個數組裡面,完成選取後把陣列中的元素全部輸出即可。

演算法實現如下

void ienum(int start, int count, int NUM, int a_len, int *a, int *tem) 
{/*start為列舉初始位置,count為選取個數計數器,NUM為選取個數,a_len為給出的陣列長度,*a為給出的陣列,*tem為暫時存放選取出的元素的陣列*/
	int i, j;
	for(i = start; i<=a_len-count; ++i)/*元素列舉範圍*/
	{
		tem[count-1] = a[i];/*把選出來的元素按序依次儲存在陣列tem[]裡面*/
		if(0 == count-1) /*判斷是否完成所需選取個數*/
		{
			for(j = NUM-1; j>=0; --j)
				printf("%d ",tem[j]);
			printf("\n");
		}
		else /*否則縮小列舉範圍,進行下一個元素的列舉選取*/
			ienum(i+1, count-1, NUM, a_len, a, tem);
	}
}

完整程式碼如下:
#include <stdio.h>
void ienum(int start, int count, int NUM, int a_len, int *a, int *tem) 
{
	int i, j;
	for(i = start; i<=a_len-count; ++i)
	{
		tem[count-1] = a[i];
		if(0 == count-1)
		{
			for(j = NUM-1; j>=0; --j)
				printf("%d ",tem[j]);
			printf("\n");
		}
		else
			ienum(i+1, count-1, NUM, a_len, a, tem);
	}
}

int main()
{
	int num, tem[6], a[6] = {1,2,3,4,5,6};
	for(num = 1; num<=6; ++num)/*從中任取1個~6個元素的所有組合*/
		ienum(0, num, num, 6, a, tem);

	getchar();
	return 0;
}

結果輸出:






1 2 
1 3 
1 4 
1 5 
1 6 
2 3 
2 4 
2 5 
2 6 
3 4 
3 5 
3 6 
4 5 
4 6 
5 6 
1 2 3 
1 2 4 
1 2 5 
1 2 6 
1 3 4 
1 3 5 
1 3 6 
1 4 5 
1 4 6 
1 5 6 
2 3 4 
2 3 5 
2 3 6 
2 4 5 
2 4 6 
2 5 6 
3 4 5 
3 4 6 
3 5 6 
4 5 6 
1 2 3 4 
1 2 3 5 
1 2 3 6 
1 2 4 5 
1 2 4 6 
1 2 5 6 
1 3 4 5 
1 3 4 6 
1 3 5 6 
1 4 5 6 
2 3 4 5 
2 3 4 6 
2 3 5 6 
2 4 5 6 
3 4 5 6 
1 2 3 4 5 
1 2 3 4 6 
1 2 3 5 6 
1 2 4 5 6 
1 3 4 5 6 
2 3 4 5 6 
1 2 3 4 5 6