題圖:遞迴
在數學與電腦科學中,遞迴是指在函式的定義中使用函式自身的方法。
遞迴演算法是一種直接或者間接地呼叫自身演算法的過程。在計算機編寫程式中,遞迴演算法對解決一大類問題是十分有效的,它往往使演算法的描述簡潔而且易於理解。
遞迴演算法解決問題的特點:
(1) 遞迴就是在過程或函式裡呼叫自身。
(2) 在使用遞迴策略時,必須有乙個明確的遞迴結束條件,稱為遞迴出口。
(3) 遞迴演算法解題通常顯得很簡潔,但遞迴演算法解題的執行效率較低。所以一般不提倡用遞迴演算法設計程式。
(4) 在遞迴呼叫的過程當中系統為每一層的返回點、區域性量等開闢了棧來儲存。遞迴次數過多容易造成棧溢位等。所以一般不提倡用遞迴演算法設計程式。在實際程式設計中尤其要注意棧溢位問題。
借助遞迴方法,我們可以把乙個相對複雜的問題轉化為乙個與原問題相似的規模較小的問題來求解,遞迴方法只需少量的程式就可描述出解題過程所需要的多次重複計算,大大地減少了程式的**量。但在帶來便捷的同時,也會有一些缺點,也即:通常用遞迴方法的執行效率不高。
講到遞迴,我們最先接觸到的乙個例項便是斐波那契數列。
斐波那契數列指的是這樣乙個數列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …
特別指出:第0項是0,第1項是第乙個1。
這個數列從第二項開始,每一項都等於前兩項之和。
斐波那契數列遞迴法實現:
intfib(intn)
if(n== 1||n ==2)
returnfib(n-1)+fib(n-2);
}
漢諾塔示意圖
漢諾塔是根據乙個傳說
最早發明這個問題的人是法國數學家愛德華·盧卡斯。以下是漢諾塔問題的遞迴求解實現:傳說印度某間寺院有三根柱子,上串64個金盤。寺院裡的僧侶依照乙個古老的預言,以上述規則移動這些盤子;預言說當這些盤子移動完畢,世界就會滅亡。這個傳說叫做梵天寺之塔問題(tower of brahma puzzle)。但不知道是盧卡斯自創的這個傳說,還是他受他人啟發。
若傳說屬實,僧侶們需要264 ? 1步才能完成這個任務;若他們每秒可完成乙個盤子的移動,就需要5849億年才能完成。整個宇宙現在也不過137億年。
這個傳說有若干變體:寺院換成修道院、僧侶換成修士等等。寺院的地點眾說紛紜,其中一說是位於越南的河內,所以被命名為「河內塔」。另外亦有「金盤是創世時所造」、「僧侶們每天移動一盤」之類的背景設定。
佛教中確實有「浮屠」(塔)這種建築;有些浮屠亦遵守上述規則而建。「河內塔」一名可能是由中南半島在殖民時期傳入歐洲的。
/*
* project : hannoi
* file : main.cpp
* author : icoding
** date & time : sat oct 06 21:01:55 2012**/
usingnamespace std;
#include #include voidhannoi (intn,char from,char buffer,char to)
else
}int main()
在電腦科學中,二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作「左子樹」(
left subtree
)和「右子樹」(
right subtree
)。一顆簡單的二叉樹
二叉樹的遍歷分為三種:前(先)序、中序、後序遍歷。
設l、d、r分別表示二叉樹的左子樹、根結點和遍歷右子樹,則先(根)序遍歷二叉樹的順序是dlr,中(根)序遍歷二叉樹的順序是ldr,後(根)序遍歷二叉樹的順序是lrd。還有按層遍歷二叉樹。這些方法的時間複雜度都是o(n),n為結點個數。
假設我們有乙個包含值的value和指向兩個子結點的left和right的樹結點結構。我們可以寫出這樣的過程:
先序遍歷(遞迴實現):
visit(node)
print node.value
ifnode.left !=null thenvisit(node.left)
ifnode.right!= nullthen visit(node.right)
中序遍歷(遞迴實現):
visit(node)
ifnode.left !=null thenvisit(node.left)
printnode.value
ifnode.right!= nullthen visit(node.right)
後序遍歷(遞迴實現):
visit(node)
ifnode.left !=null thenvisit(node.left)
ifnode.right!= nullthen visit(node.right)
print node.value
問題:
寫乙個函式返回乙個串的所有排列。
解析:
對於乙個長度為n的串,它的全排列共有a(n, n)=n!種。這個問題也是乙個遞迴的問題, 不過我們可以用不同的思路去理解它。為了方便講解,假設我們要考察的串是」abc」, 遞迴函式名叫permu。
思路一:
我們可以把串「abc」中的第0個字元a取出來,然後遞迴呼叫permu計算剩餘的串「bc」 的排列,得到。然後再將字元a插入這兩個串中的任何乙個空位(插空法), 得到最終所有的排列。比如,a插入串bc的所有(3個)空位,得到。 遞迴的終止條件是什麼呢?當乙個串為空,就無法再取出其中的第0個字元了, 所以此時返回乙個空的排列。**如下:
typedef vectorvs;
vs permu(string s)
string c = s.substr(0, 1);
vs res = permu(s.substr(1));
for(int i=0; i
思路二:
我們還可以用另一種思路來遞迴解這個問題。還是針對串「abc」, 我依次取出這個串中的每個字元,然後呼叫permu去計算剩餘串的排列。 然後只需要把取出的字元加到剩餘串排列的每個字元前即可。對於這個例子, 程式先取出a,然後計算剩餘串的排列得到,然後把a加到它們的前面,得到 ;接著取出b,計算剩餘串的排列得到,然後把b加到它們前面, 得到;後面的同理。最後就可以得到「abc」的全序列。**如下:
vs permu1(string s)
for(int i=0; i
更詳細講解請參考:寫乙個函式返回乙個串的所有排列
問題:
經典的八皇后問題,即在乙個8*8的棋盤上放8個皇后,使得這8個皇后無法互相攻擊( 任意2個皇后不能處於同一行,同一列或是對角線上),輸出所有可能的擺放情況。
解析:
8皇后是個經典的問題,如果使用暴力法,每個格仔都去考慮放皇后與否,一共有264 種可能。所以暴力法並不是個好辦法。由於皇后們是不能放在同一行的, 所以我們可以去掉「行」這個因素,即我第1次考慮把皇后放在第1行的某個位置, 第2次放的時候就不用去放在第一行了,因為這樣放皇后間是可以互相攻擊的。 第2次我就考慮把皇后放在第2行的某個位置,第3次我考慮把皇后放在第3行的某個位置, 這樣依次去遞迴。每計算1行,遞迴一次,每次遞迴裡面考慮8列, 即對每一行皇后有8個可能的位置可以放。找到乙個與前面行的皇后都不會互相攻擊的位置, 然後再遞迴進入下一行。找到一組可行解即可輸出,然後程式回溯去找下一組可靠解。
我們用乙個一維陣列來表示相應行對應的列,比如c[i]=j表示, 第i行的皇后放在第j列。如果當前行是r,皇后放在哪一列呢?c[r]列。 一共有8列,所以我們要讓c[r]依次取第0列,第1列,第2列……一直到第7列, 每取一次我們就去考慮,皇后放的位置會不會和前面已經放了的皇后有衝突。 怎樣是有衝突呢?同行,同列,對角線。由於已經不會同行了,所以不用考慮這一點。 同列:c[r]==c[j]; 同對角線有兩種可能,即主對角線方向和副對角線方向。 主對角線方向滿足,行之差等於列之差:r-j==c[r]-c[j]; 副對角線方向滿足, 行之差等於列之差的相反數:r-j==c[j]-c[r]。 只有滿足了當前皇后和前面所有的皇后都不會互相攻擊的時候,才能進入下一級遞迴。
**如下:
#include using namespace std;
int c[20], n=8, cnt=0;
void print()
cout<
}cout<
}void search(int r)
for(int i=0; i
if(ok) search(r+1);
}}int main()
遞迴演算法的講解
在正式介紹遞迴之前,我們首先引用知乎使用者李繼剛 對遞迴和迴圈的生動解釋 遞迴 你開啟面前這扇門,看到屋裡面還有一扇門。你走過去,發現手中的鑰匙還可以開啟它,你推開門,發現裡面還有一扇門,你繼續開啟它。若干次之後,你開啟面前的門後,發現只有一間屋子,沒有門了。然後,你開始原路返回,每走回一間屋子,你...
例項講解RSA演算法
圖為 rsa公開金鑰演算法的發明人,從左到右ron rivest,adi shamir,leonard adleman.攝於1978年 素數是這樣的整數,它除了能表示為它自己和1的乘積以外,不能表示為任何其它兩個整數的乘積。例如,15 3 5,所以15不是素數 又如,12 6 2 4 3,所以12也...
遞迴演算法例項
如 求1 2 3 4 n 1.方法 public static int operation int n 注意返回值和引數 2.根據提議,當值為1時表示不再遞迴,那麼此處定義為程式出口,即 if n 1 如果未到達出口,那麼進行遞迴運算 else if n 1 這裡注意的地方有 operation n...