遞迴(一)幾個簡單的遞迴例子
剛接觸遞迴的同學,可能難以理解遞迴,難以理解的點可能很多,例如:
1.函式為什麼可以在自己的內部又呼叫自己呢?
2.既然可以自己呼叫自己,那麼遞迴執行過程中一定回有很多層相互巢狀,到底什麼時候不再巢狀呢?
3.遞迴執行過程中,相互巢狀的多層之間會有引數傳遞,多層之間是否會相互影響?
遞迴兩個要素
1.遞迴邊界
2.遞迴的邏輯——遞迴"公式"
遞迴的過程一定有引數的變化,並且引數的變化,和遞迴邊界有關係.
在難度較大的題目中,這兩者均不容易直接得到.
遞迴的種種問題,也許理解的同學可能可以用一句話解釋清楚,但是不理解的同學再怎麼說也沒辦法理解.
下面通過幾個簡單的例子【體會】一下遞迴,先從【感性】的角度理解遞迴.
1.Fibonacci數
我們直到Fibonacci數的遞推公式為:F(0)=F(1)=1,F(n)=F(n-1)+F(n-2) n>=2;
這個明顯地給出了遞迴邊界n=0或1的時候F(n)的值,和遞迴邏輯F(n)=F(n-1)+F(n-2),即遞推公式.所以這個遞迴函式不難書寫
#include<iostream> using namespace std; int F(int n)//函式返回一個數對應的Fibonacci數 { if(n==0 || n==1)//遞迴邊界 return 1; return F(n-1) + F(n-2);//遞迴公式 } int main() { //測試 int n; while(cin >> n) cout << F(n) << endl; return 0; }
2.階乘
階乘的遞迴公式為:
程式碼如下:
#include<iostream>
using namespace std;
int F(int n)
{
if(n==0)//遞迴邊界
return 1;
return n*F(n-1);//遞迴公式
}
int main()
{
int n;
cin >> n;
cout << F(n) << endl;
return 0;
}
3.陣列求和
給一個數組a[]:a[0],a[1],...,a[n-1]如何用遞迴的方式求和?
仍然是兩個問題:遞迴邊界和遞迴公式.
遞迴邊界是什麼?一時不容易想到,但是我們想到了求和,多個數的求和過程是什麼,x,y,z,w手動求和的過程是什麼?步驟如下:
x+y=a,任務變為a,z,w求和
a+z=b,任務變為b,w求和
b+w=c得出答案
思考一下,【得出答案】這一步為什麼就可以得出答案呢?(廢話?)是因為,一個數不用相加就能得出答案.
所以,遞迴的邊界就是隻有一個數.
所以,遞迴邊界有了,那麼遞迴公式呢?其實手動計算過程中,隱含了遞迴公式:
其中+為求兩個數的和,F為求多個數的和的遞迴函式.程式碼如下:
#include<iostream>
using namespace std;
int F(int a[],int start,int end)
{
if(start==end)//遞迴邊界
return a[start];
return a[start] + F(a,start+1,end);//遞迴公式
}
int main()
{
int a[] = {1,2,3,4,5};
int s=0,e=4;
cout << F(a,s,e) << endl;
return 0;
}
4.求陣列元素最大值
手動求最大值的過程是什麼,遍歷+比較,過程如下:
例如,求3,2,6,7,2,4的最大值:先設定最大值max=-999999,然後將max和陣列元素逐個(遍歷)比較如果a[i]>max,則更新max的值為a[i],否則max不變,繼續向後遍歷,直到遍歷結束.
max<3,則max=3
max>2,max=3不變
max<6,則max=6
max<7,則max=7
max>2,max=7不變
max>4,max=7不變
遍歷結束,max=7為最大值.
和求和類似,遞迴的公式如下:
其中max為求兩個數的較大值函式,F為求多個數的最大值的遞迴函式.程式碼如下:
#include<iostream>
using namespace std;
#define max(a,b) (a>b?a:b)
int F(int a[],int s,int e)
{
if(s==e)
return a[s];
else if(s+1 == e)//遞迴邊界
return max(a[s],a[e]);
return max(a[s],F(a,s+1,e));//遞迴公式!!!
}
int main()
{
int a[] = {5,1,4,6,2};
int s = 0,e = 4;
cout << F(a,s,e) << endl;
return 0;
}
之所以,說上面的幾個例子是【簡單例子】,是因為上述所有的遞迴都屬於【單向遞迴】.單向遞迴,遞迴的路徑就是一個方向,所以思路相對比較容易想到.
較難的遞迴問題,一般都不是單向遞迴,而是需要使用【回溯】的方法,遞迴的方法不太容易想到.