首先你得理解遞迴的作用或目的。當一個問題比較複雜時,我們設法將他轉化為一個或多個形式一樣但問題規模較小的問題來解決,並且當小問題解決後能夠推匯出大問題的解。若小問題仍然無法解決則繼續將小問題轉化為形式相同的更小問題,直到問題規模小到足以輕而易舉地解決。這裡將一個大問題轉化或者說分解出的小問題稱為該大問題的子問題。
而迴圈中的遞迴可以認為是在分解問題的過程中將問題分解為了多個而不是一個子問題,而原問題的解是這多個子問題的解的和(或組合、合集之類的),所以你要用迴圈來將子問題的解進行求和或組合。
在你給的這個例子中,由於求的是組合,那麼可以知道123和312是相同的組合,所以只需求其中的一種,這裡是規定只求組合中的數是遞增的那種。因此對測試資料{1,2,3,4,5,6}可以這麼理解這個問題:
{1,2,3,4,5,6}中取4個數的所有組合 =
第一個數取1,後三個數為{2,3,4,5,6}中取3個數的所有組合
+ 第一個數取2,後三個數為{3,4,5,6}中取3個數的所有組合
+ 第一個數取3,後三個數為{4,5,6}中取3個數的所有組合
這裡當三個子問題求解出來以後,你就需要用迴圈將他們加起來不是麼!
注意這裡的三個子問題分別是{2,3,4,5,6}中取3個數的所有組合、{3,4,5,6}中取3個數的所有組合、{4,5,6}中取3個數的所有組合。這樣看他們才和原問題有相同的形式。那麼將第一個子問題再進行分解:
{2,3,4,5,6}中取3個數的所有組合 =
第一個數取2(全域性來看是第二個數取2),後2個數為{3,4,5,6}中取2個數的所有組合
+ 第一個數取3(全域性來看是第二個數取3),後2個數為{4,5,6}中取2個數的所有組合
+ 第一個數取4(全域性來看是第二個數取4),後2個數為{5,6}中取2個數的所有組合
以此類推……
當遞迴到了某種情況,比如:第一個數取4(全域性的第四個數取4),後0個數為{5,6}中取0個數的所有組合,那麼這個子問題是不是輕而易舉地解決了呢,因為取0個數表示你什麼都不要做就將它解決了對吧,那麼這時將前面選的四個數輸出就是原始問題所要的其中的一個組合了。
下面講下程式碼吧。
//arr為原始陣列
//start為遍歷起始位置
//result儲存結果,為一維陣列
//count為result陣列的索引值,起輔助作用(也可理解為剩餘還要選擇幾個數)
//NUM為要選取的元素個數
//arr_len為原始陣列的長度,為定值
//============
由於每次遞迴呼叫時arr、result、NUM、arr_len四個引數的位置都是原封不動的傳遞下去,因此可以為了方便將它們作為全域性變數,從而將他們移除出函式的引數列表
void combine_increase(int start, int count)//在start到末尾的這個集合中選取count個數的組合
{
int i = 0;
for (i = start; i
result[count - 1] = i;//第一個數選擇i(子問題中的第一個,全域性的第NUM-count+1個),這裡他選擇倒著存放,即當還有四個數要選時,選擇的第一個數放在result[3];當還有1個數要選時,選擇的第四個即最後一個數放在result[0]
if (count - 1 == 0)//若在選擇了一個數後剩餘要選擇的數的數量為0
{//那麼可以輸出了
int j;
for (j = NUM - 1; j >= 0; j--)//倒序遍歷result輸出吧
printf("%d\t",arr[result[j]]);
printf("\n");
}
else//還沒選完?
combine_increase( i + 1, count - 1);//在i+1到末尾的這個集合中選取count-1個數的組合
//當函式返回時表示一個子問題得以解決了,那麼返回上一層遞迴,迴圈中的i++,即選取下一個數作為上一層那個問題中的第一個數,去解決另一個子問題吧!
首先你得理解遞迴的作用或目的。當一個問題比較複雜時,我們設法將他轉化為一個或多個形式一樣但問題規模較小的問題來解決,並且當小問題解決後能夠推匯出大問題的解。若小問題仍然無法解決則繼續將小問題轉化為形式相同的更小問題,直到問題規模小到足以輕而易舉地解決。這裡將一個大問題轉化或者說分解出的小問題稱為該大問題的子問題。
而迴圈中的遞迴可以認為是在分解問題的過程中將問題分解為了多個而不是一個子問題,而原問題的解是這多個子問題的解的和(或組合、合集之類的),所以你要用迴圈來將子問題的解進行求和或組合。
在你給的這個例子中,由於求的是組合,那麼可以知道123和312是相同的組合,所以只需求其中的一種,這裡是規定只求組合中的數是遞增的那種。因此對測試資料{1,2,3,4,5,6}可以這麼理解這個問題:
{1,2,3,4,5,6}中取4個數的所有組合 =
第一個數取1,後三個數為{2,3,4,5,6}中取3個數的所有組合
+ 第一個數取2,後三個數為{3,4,5,6}中取3個數的所有組合
+ 第一個數取3,後三個數為{4,5,6}中取3個數的所有組合
這裡當三個子問題求解出來以後,你就需要用迴圈將他們加起來不是麼!
注意這裡的三個子問題分別是{2,3,4,5,6}中取3個數的所有組合、{3,4,5,6}中取3個數的所有組合、{4,5,6}中取3個數的所有組合。這樣看他們才和原問題有相同的形式。那麼將第一個子問題再進行分解:
{2,3,4,5,6}中取3個數的所有組合 =
第一個數取2(全域性來看是第二個數取2),後2個數為{3,4,5,6}中取2個數的所有組合
+ 第一個數取3(全域性來看是第二個數取3),後2個數為{4,5,6}中取2個數的所有組合
+ 第一個數取4(全域性來看是第二個數取4),後2個數為{5,6}中取2個數的所有組合
以此類推……
當遞迴到了某種情況,比如:第一個數取4(全域性的第四個數取4),後0個數為{5,6}中取0個數的所有組合,那麼這個子問題是不是輕而易舉地解決了呢,因為取0個數表示你什麼都不要做就將它解決了對吧,那麼這時將前面選的四個數輸出就是原始問題所要的其中的一個組合了。
下面講下程式碼吧。
//arr為原始陣列
//start為遍歷起始位置
//result儲存結果,為一維陣列
//count為result陣列的索引值,起輔助作用(也可理解為剩餘還要選擇幾個數)
//NUM為要選取的元素個數
//arr_len為原始陣列的長度,為定值
//============
由於每次遞迴呼叫時arr、result、NUM、arr_len四個引數的位置都是原封不動的傳遞下去,因此可以為了方便將它們作為全域性變數,從而將他們移除出函式的引數列表
//============
void combine_increase(int start, int count)//在start到末尾的這個集合中選取count個數的組合
{
int i = 0;
for (i = start; i
{
result[count - 1] = i;//第一個數選擇i(子問題中的第一個,全域性的第NUM-count+1個),這裡他選擇倒著存放,即當還有四個數要選時,選擇的第一個數放在result[3];當還有1個數要選時,選擇的第四個即最後一個數放在result[0]
if (count - 1 == 0)//若在選擇了一個數後剩餘要選擇的數的數量為0
{//那麼可以輸出了
int j;
for (j = NUM - 1; j >= 0; j--)//倒序遍歷result輸出吧
printf("%d\t",arr[result[j]]);
printf("\n");
}
else//還沒選完?
combine_increase( i + 1, count - 1);//在i+1到末尾的這個集合中選取count-1個數的組合
}
//當函式返回時表示一個子問題得以解決了,那麼返回上一層遞迴,迴圈中的i++,即選取下一個數作為上一層那個問題中的第一個數,去解決另一個子問題吧!
}