(算法)遞歸各習題總結反思 *基礎


這里先搞清楚遞歸的基本思想,遞歸函數的運行過程,暫且不考慮是否使用遞歸在效率方面的差異。

題目 1:

數的組合問題。從1,2,…,n中取出m個數,將所有組合按照字典順序列出。如n=3,m=2時,輸出:12 13 23

 1 #include <stdio.h>
 2 int Function(int n,int m,int a[],int depth,int output[],int size,int x) //n即指有n個數,m為從n個數中選取的個數,a[]為所選取的數組,depth遞歸進入深度,output答案,size為大小,x為當前掃描到的位置 
 3 {
 4     if(m == 0)
 5     {
 6         int i;
 7         for(i = 0;i < size;i++)
 8         {
 9             printf("%d ",output[i]);
10         }
11         printf("\n");
12         return 1;
13     }else
14     {
15         int i;
16         for(i = x;i <= n - m;i++)
17         {
18             output[depth] = a[i];
19             Function(n,m - 1,a,depth + 1,output,size,i + 1);
20         }
21     }
22 }
23 int main ()
24 {
25     int output[2];
26     int a[5] = {1,2,3,4,5};
27     Function(5,2,a,0,output,2,0);
28     return 0;
29 }
View Code

本題還有不完善之處(參數設置,遞歸方法等),需繼續改進。

 

題目 2:

小猴子第一天摘下若干桃子,當即吃掉一半,又多吃一個.第二天早上又將剩下的桃子吃一半,又多吃一個.以后每天早上吃前一天剩下的一半另一個.到第10天早上猴子想再吃時發現只剩下一個桃子了問第一天猴子共摘多少個桃子?

 1 #include <stdio.h>
 2 int Peachcount(int day)
 3 {
 4     if(day == 10)
 5     return 1;
 6     return (Peachcount(day + 1)+1) * 2;
 7 }
 8 int main () 
 9 {
10     int day = 1;
11     printf("第一天的桃子個數:%d\n",Peachcount(1));
12     return 0;
13 }
View Code

題目3:

有雌雄一對兔子,假定過兩個月便可每個月繁殖雌雄各一的一對小兔子。問過n個月后共有多少對兔子?

 1 #include <stdio.h>
 2 int Rabbitcount(int month)
 3 {
 4     if(month == 0 || month == 1)
 5     return 1;
 6     return Rabbitcount(month - 1) + Rabbitcount(month - 2);
 7 } 
 8 int main ()
 9 {
10     int month;
11     printf("請輸入過了多少個月:\n");
12     scanf("%d",&month);
13     printf("兔子的一共的對數:%d",Rabbitcount(month));
14     return 0;
15 }
View Code

題目4:

一個人趕着鴨子去每個村庄賣,每經過一個村子賣去所趕鴨子的一半又一只。這樣他經過了七個村子后還剩兩只鴨子,問他出發時共趕多少只鴨子?經過每個村子賣出多少只鴨子?

 1 #include <stdio.h>
 2 int Duckcount(int village)
 3 {
 4     if(village == 7)
 5     return 2;
 6 //    return Duckcount(village - 1) / 2 - 1;
 7     return (Duckcount(village + 1) + 1) * 2;
 8 }
 9 int main ()
10 {
11     int village;
12     printf("請輸入經過的村庄個數:\n");
13     scanf("%d",&village);
14     int i;
15     for(i = village;i >= 0;i--)
16     {
17         printf("經過第%d個村庄鴨子數:%d\n",i,Duckcount(i));
18     }
19     return 0;
20 }
View Code

題目5:

著名的菲波拉契(Fibonacci)數列,其第一項為0,第二項為1,從第三項開始,其每一項都是前兩項的和。編程求出該數列前N項數據。

 1 #include <stdio.h>
 2 int Fib(int n)
 3 {
 4     if(n == 0)
 5     return 0;
 6     if(n == 1)
 7     return 1;
 8     return Fib(n-1) + Fib(n-2);
 9 }
10 int main ()
11 {
12     int n;
13     printf("請輸入所求項數:\n");
14     scanf("%d",&n);
15     int i;
16     for(i = 0;i <= n;i++)
17     {
18         printf("第%d項:%d\n",i,Fib(i));
19     }
20     return 0;
21 }
View Code

題目6:

輸入一個數,求這個數的各位數字之和。

 1 #include <stdio.h>
 2 int Countsum(int num,int n)
 3 {
 4     int mask = 1;
 5     int i;
 6     for(i = 0;i < n;i++)
 7     {
 8         mask = mask * 10;
 9     }
10     mask = mask / 10;
11     if(n == 1)
12     return num;
13     return Countsum((num % mask),n -1 ) + num / mask;  //因為這一步所以0不會干擾 
14 } 
15 int main ()
16 {
17     int input;
18     int n = 0;        //計算位數
19     printf("請輸入您的數字:\n");
20     scanf("%d",&input);
21     int temp = input;
22     while(temp > 0)
23     {
24         temp = temp / 10;
25         n++;
26     } 
27     printf("該數字的各位的和:\n%d\n",Countsum(input,n));
28     return 0;
29 }
View Code

題目7:

角谷定理。輸入一個自然數,若為偶數,則把它除以2,若為奇數,則把它乘以3加1。經過如此有限次運算后,總可以得到自然數值1。求經過多少次可得到自然數1。如:輸入22,輸出

22  11  34  17  52  26  13  40  20  10  5  16  8  4  2  1 

STEP=16

錯誤代碼

 1 #include <stdio.h>
 2 //重點總結如何將SETP表示出來,為何出錯,失誤點? 
 3 void Function(int *step,int n)
 4 {
 5     if(n == 1)
 6     return  ;
 7     if( n % 2 == 0)
 8     {
 9         printf("%d ",n / 2);
10         *step++;
11         Function(step,n / 2);
12     }else
13     if(n % 2 != 0)
14     {
15         printf("%d ",3 * n + 1);
16         *step++;
17         Function(step,3 * n + 1);
18     }
19     
20 } 
21 int main ()
22 {
23     int n;
24     int step = 0;
25     printf("請輸入您的數字:\n");
26     scanf("%d",&n);
27     Function(&step,n);
28     printf("總共的步數:\n%d",step);
29     return 0;
30 }
View Code

 正確代碼

 1 #include <stdio.h>
 2 //重點總結如何將SETP表示出來,為何出錯,失誤點? 
 3 void Function(int *step,int n)          //因為傳入的參數需要經過變化所以一定要用指針!! 
 4 {
 5     if(n == 1)
 6     {
 7         printf("%d",n);
 8         return  ;
 9     }
10     if( n % 2 == 0)
11     {
12         printf("%d ",n);
13         (*step)++;                    //自加的優先級比*更高所以必須加上括號 
14         Function(step,n / 2);
15     }else
16     if(n % 2 != 0)
17     {
18         printf("%d ",n);
19         (*step)++;
20         Function(step,3 * n + 1);
21     }
22     
23 } 
24 int main ()
25 {
26     int n;
27     int step = 0;
28     printf("請輸入您的數字:\n");
29     scanf("%d",&n);
30     printf("變化過程:");
31     Function(&step,n);
32     printf("\n");
33     printf("總共的步數:%d",step);
34     return 0;
35 }
View Code

 題目8:

梯有N階,上樓可以一步上一階,也可以一次上二階。編一個程序,計算共有多少種不同的走法。

 1 #include <stdio.h>
 2 int Function(int step,int *pathsum)
 3 {
 4     if(step < 0)
 5     {
 6         return *pathsum;
 7     }else
 8     if(step == 0)
 9     {
10         (*pathsum)++;
11         return *pathsum;
12     }else
13     Function(step - 1,pathsum);
14     Function(step - 2,pathsum);
15     return *pathsum;
16 }
17 int main ()
18 {
19     int pathsum = 0;
20     int step;
21     printf("請輸入一共有的樓梯階數:\n");
22     scanf("%d",&step);
23     printf("共有的走法種數:\n");
24     printf("%d",Function(step,&pathsum));
25     return 0;
26 }
View Code

首先想到的方法一:

 

設S(n) 為階梯數量,可以容易推出:S(n - 1) = S(n) - 1;或者 S(n - 2) = S(n) - 2;  基准情況: S(0) 和 S(<0)

從輸入的階梯數量n開始,每次調用函數都會讓階梯數 - 1或者 - 2,當階梯數減到基准情況S = 0 時,說明這一遞歸過程正確(正好走完階梯),將路線數pathsum + 1並返回。

當s < 0 時;說明這一遞歸路線錯誤(走多了樓梯)將路線數pathsum 返回,不對其改動。

 

搜索相關資料后,方法2:斐波那契的應用

 

題目9:

某人寫了n封信和n個信封,如果所有的信都裝錯了信封。求所有的信都裝錯信封共有多少種不同情況? 

題目10:

給定一個整數n,輸出這個整數拆分的可能總數

 1 #include <stdio.h>
 2 int q(int n,int m)           //n為要拆分的數字,m為所拆部分中最大值 
 3 {
 4     if(n == 1|| m == 1)
 5     {
 6         return 1;
 7     }else
 8     if(n < m)
 9     {
10         return q(n,n);
11     }else
12     if(n == m)
13     {
14         return 1 + q(n,n - 1);
15     }else
16     if(n > m)
17     {
18         return q(n,m-1)+q(n - m,m);
19     }
20 }
21 int main ()
22 {
23     printf("請輸入要拆分的數字\n");
24     int n;
25     scanf("%d",&n);
26     int ret = 0;
27     ret = q(n,n);
28     printf("該數字一共有的拆分方法數\n");
29     printf("%d",ret);
30     return 0;
31 }
View Code

此題一開始沒有思路,首先找到了 n = 1 時的方法總數,接着思考 q(n)與q(n - 1)的關系難以找到關系,卡住。

查閱網上資料后得到的思路:(http://blog.chinaunix.net/uid-26548237-id-3503956.html)

    所謂整數划分,是指把一個正整數n寫成如下形式:

    n=m1+m2+m3+....+mi;(其中mi為正整數,並且1<=mi<=n),則{m1,m2,m3,....,mi}為n的一個划分。

    如果{m1,m2,m3,....,mi}中的最大值不超過m,即max{m1,m2,m3,....,mi} <= m,則稱它屬於n的一個m划分。這里我們記n的m划分的個數為f(n,m);

    例如當n=4時,它有5個划分:{4}、{3,1}、{2,2}、{2,1,1}、{1,1,1,1};

    注意:4=1+3和4=3+1被認為是同一個划分。

    該問題是求出n的所有划分個數,即f(n,n)。下面我們考慮求f(n,m)的方法。

    遞歸法

    根據n和m的關系,考慮下面幾種情況:

    (1)當n=1時,不論m的值為多少(m>0),只有一種划分,即{1};

    (2)當m=1時,不論n的值為多少(n>0),只有一種划分,即{1,1,....1,1,1};

    (3)當n=m時,根據划分中是否包含n,可以分為兩種情況:

        (a)划分中包含n的情況,只有一個,即{n};

        (b)划分中不包含n的情況,這時划分中最大的數字也一定比n小,即n的所有(n-1)划分;

        因此,f(n,n) = 1 + f(n, n - 1)。

    (4)當n<m時,由於划分中不可能出現負數,因此就相當於f(n,n);< span="">

    (5)當n>m時,根據划分中是否包含m,可以分為兩種情況:

        (a)划分中包含m的情況,即{m,{x1,x2,x3,...,xi}},其中{x1,x2,x3,...,xi}的和為n-m,可能再次出現m,因此是(n-m)的m划分,因此這種划分個數為f(n-m, m);

        (b)划分中不包含m的情況,則划分中所有值都比m小,即n的(m-1)划分,個數為f(n, m - 1);

        因此,f(n,m) = f(n - m,m) + f(n, m - 1)。

 

    綜合以上各種情況,可以看出,上面的結論具有遞歸定義的特征,其中(1)和(2)屬於回歸條件,(3)和(4)屬於特殊情況,而情況(5)為通用情況,屬於遞歸的方法,其本質主要是通過減少n或m以達到回歸條件,從而解決問題。

    其遞歸表達式如下所示。

 

題目 11 :數的全排列問題

 1 #include <stdio.h>
 2 void swap(int *a,int *b)
 3 {
 4     int temp;
 5     temp = *a;
 6     *a = *b;
 7     *b = temp;
 8 }
 9 void Permutation(int input[],int size,int depth)
10 {
11     if(depth == size - 1)
12     {
13         int i;
14         for(i = 0;i < size;i ++)
15         {
16             printf("%d ",input[i]);
17         }
18         printf("\n");
19         return ;
20     }else
21     if(depth < size - 1)
22     {
23         int i;
24         for(i = depth;i < size;i++)
25         {
26             swap(&input[depth],&input[i]);
27             Permutation(input,size,depth + 1);
28             swap(&input[depth],&input[i]);
29         }
30     }
31 } 
32 int main()
33 {
34     int input[4] = {1,3,5,7};
35     printf("1,3,5,7全排列:\n");
36     Permutation(input,4,0);
37     return 0;
38 }
View Code

思路:例 1,2,3 進行全排列 當數據規模為1時 容易得到那一個數即為所求者,若有兩個數字,1,2則所求者為1,2或者2,1

可以推斷出全排列即為每一位上的數字依次換成后面的數字({1,2,3},第一位1要換成第1,2,3位的數字,第二位要換成第2,3位的數字,第三位換成第三位數字)

注意換完位置后必須要換回原位(如下圖右邊線路 1,2,3 ->1,3,2返回前要將3,2換回原來的位置1,2,3供上一層依次調用才不出錯)

題目12

要求找出具有下列性質數的個數(包含輸入的自然數n):
先輸入一個自然數n(n<=1000),然后對此自然數按照如下方法進行處理:
1. 不作任何處理;
2. 在它的左邊加上一個自然數,但該自然數不能超過原數的一半;
3. 加上數后,繼續按此規則進行處理,直到不能再加自然數為止. 

輸入

一個自然數n

輸出

一個數,表示滿足條件的數的個數
樣例輸入
6
樣例輸出
6

 1 #include <stdio.h>
 2 int sum = 0;           //收集返回的結果 
 3 int Function (int n)
 4 {             
 5     int mask = 1;
 6     int temp;
 7     temp = n;
 8     while(temp > 0)
 9     {
10         mask = mask * 10;
11         temp = temp / 10;
12     }
13     mask = mask / 10;
14     int initialnum;
15     initialnum = n / mask;
16     if(initialnum == 1)
17     return 1;
18     int i;
19     for(i = 1;i <= initialnum / 2;i++)
20     {
21         sum = sum + Function(n + mask * 10 * i); 
22     }
23     return sum;
24 }
25 int main ()
26 {
27     int n;
28     printf("請輸入數字:\n");
29     scanf("%d",&n);
30     int ret;
31     ret = Function(n);
32     printf("方法數:\n%d",ret);
33     return 0;
34 }
View Code

此題一開始疑惑函數如何返回,最后解決方案是設sum為全局變量

題目 13

用遞歸的方法求N個數中最大的數及其位置。

 1 #include <stdio.h>
 2 int Compare(int a,int b)
 3 {
 4     if(a >= b)
 5     {
 6         return a;
 7     }
 8     return b;
 9 }
10 int Findmax(int input[],int size,int depth)
11 {
12     if(depth == size - 1)
13     {
14         return input[depth];
15     }else
16     return Compare(input[depth],Findmax(input,size,depth + 1));
17 } 
18 int main ()
19 {
20     int a[6] = {2,4,6,4,8,7};
21     int ret;
22     ret = Findmax(a,6,0);
23     printf("%d",ret);
24     return 0;
25 }
View Code

 

對以上各題的反思總結:

遇到問題1.先觀察是否能得到問題小規模化時的解決方法。

            2.分析問題規模慢慢變大時其解決方法的過程是否相同。

    3.分析問題之間的遞歸關系。(難點在此處,遞歸關系的發現是關鍵之處)

    4.將1作為邊界,列出關系式。編程解決之。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM