1. 程式人生 > >m選n組合的兩種演算法(C語言實現)

m選n組合的兩種演算法(C語言實現)

原問題:

 Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.

1. 遞迴演算法

即首先選擇n,然後遞迴地從剩下的1...n-1選擇k-1個數,然後選擇n-1,然後遞迴地從剩下的1...n-2選擇k-1個數,直到選到k。

//d儲存選擇的數,NUM指示選擇多少個數,即初始k的大小
void Recursive_SelectK(int n,int k,int d[],const int NUM)
{
	int i = n;
	if(k > n) return;
	while(i>=k)
	{
		d[NUM-k] = i;
		if(k>1) Recursive_SelectK(i-1,k-1,d,NUM);
		else
		{
			int j = 0;
			while(j<NUM)
			{
				printf("%d ",d[j]);
				j++;
			}
			printf("\n");
		}
		i--;
	}
}

2. 01轉換演算法

首先初始化一個n大小的陣列,0代表未選擇,1代表選擇,每次都有k個1,n-k個0,當把所有的01組合列出,即是n選k的所有組合。得到所有01組合的演算法描述如下:

  • 首先初始化,將陣列前k個元素置1,表示第一個組合為前k個數。
  • 然後從左到右掃描陣列元素值的“10”組合,找到第一個“10”組合後將其變為“01”組合;
  • 同時將其左邊的所有“1”全部移動到陣列的最左端。
  • 當第一個“1”移動到陣列的n-k的位置,即n個“1”全部移動到最右端時,就得到了最後一個組合。
void SelectK_01(int n,int k)
{
	//true is selected
	bool *nselect = (bool *)malloc(n*sizeof(bool));
	
	int i = 0;
	
	//初始化第一個組合,即選擇前k個數 
	while(i<n)
	{
		if(i<k)
		 nselect[i] = true;
	    else
		 nselect[i] = false;
		i++;
	}
	
	i = 0;
	while(i<n){
		if(nselect[i]) printf(" %d ",i+1);
		i++;
	}
	printf("\n");
	
	i=0;
	while(i<n-1)
	{
		//找到第一個10組合,並交換10的位置 
		if(nselect[i] && !nselect[i+1])
		{
			int temp = nselect[i];
			int x=0,y=0;
			nselect[i] = nselect[i+1];
			nselect[i+1] = temp;
			//將該組合左邊所有的1移至陣列最左邊 
			while(y<i)
			{
				if(nselect[y])
				{
					temp = nselect[y];
					nselect[y] = nselect[x];
					nselect[x] = temp;
					x++;
				}
				y++;
			}
			
			i = 0;
			while(i<n){
				if(nselect[i]) printf(" %d ",i+1);
				i++;
			}
			printf("\n");
			
			i = (x==0?0:x-1);
		}
		else i++; 
	}
	free(nselect);
}