資料結構學習(C++)——遞迴【1】 (轉)

gugu99發表於2007-08-15
資料結構學習(C++)——遞迴【1】 (轉)[@more@]

按照黃皮書的安排,到了該講遞迴的時候了。上網查了查,關於“遞迴”的文章可以說“汗牛充棟”——請原諒我在這裡犯酸,我的意思是,寫別人都寫臭的東西讓大家看,只是浪費大家的時間,所以我下面的東西應該是一些至少我看起來是新的東西,如果覺得有什麼不清楚的,請參閱相關的文章(太多了)。即使這樣,這篇文章還是不能把我想說的寫完,看來我這人真的有廢話的習慣。

看過這樣一道題,問,“結構化設計的三種基礎結構,順序、選擇、迴圈是不是必須的?”當然,你知道這樣一個論斷,只要有這三種就足夠了;但是能不能更少呢?答案是“可以”,原因就是遞迴能取代迴圈的作用,例如下面的對一個陣列裡面元素求和的:

float rsum (float a[], const int n)

{

  if (n <= 0) return 0;

  else return rsum(a, n – 1) + a[n – 1];

}

實際上就是:

sum = 0;

for (int i = 0; i < n; i++) sum += a[i];

但實際的情況是,任何的一種語言裡面都有迴圈結構,但不是任何的語言都支援遞迴;套用一句話,遞迴是萬能的,但沒有遞迴不是萬萬不能的。然而,我看到現在的某些人,不管什麼問題都要遞迴,明明迴圈是第一個想到的方法,偏偏費盡腦筋去尋找遞迴演算法。對此,我真的不知道該說什麼。

遞迴是演算法嗎

經常的看到“遞迴演算法”、“非遞迴演算法”,這種提法沒有語義上的問題,並且我自己也這樣用——遞迴的演算法。但這也正說明了,遞迴不是演算法,他是一種思想,正是因為某個演算法的指導思想是遞迴的,所以才被稱為遞迴演算法;而一個有遞迴演算法的問題,當你不使用遞迴作為指導思想,這樣得到的演算法就是非遞迴演算法。——而對於迴圈能處理的問題,都有遞迴解法,在這個意義上說,迴圈演算法都可以稱為非遞迴演算法。

我在這咬文嚼字沒什麼別的意思,只是想讓大家知道,能寫出什麼樣的演算法,關鍵是看你編寫演算法時的指導思想。如果一開始就想到了迴圈、迭代的方法,你再費心耗神去找什麼遞迴演算法——即使找到了一種看似“簡潔”的演算法,由於他的低效實際上還是廢物——你還在做這種無用功幹什麼?典型的學究陋習。如果你僅僅想到了遞迴的方法,現在你想用棧來消解掉遞迴,你做的工作僅僅是把做的事自己做了,你又能把提高多少?盲目的迷信消解遞迴就一定能提高效率是無根據的——你做的工作的方法如果不得當的話,甚至還不如系統原來的做法。

從學排列組合那天開始,我所知道的階乘就是這個樣子n! = 1×2×……n。如果讓我來寫階乘的演算法,我也只會想到從1乘到n。再如,斐波那契數列,如果有人用自然語言描述的話,一定是這樣的,開始兩項是0、1,以後的每項都是前面兩項的和。所以讓我寫也只會得到“儲存前兩項,然後相加得到結果”的迭代解法。——現在只要是講到遞迴幾乎就有他們的登場,美其名曰:“定義是遞迴的,所以我們寫遞迴演算法”。我想問的是,定義的遞迴抽象是從哪裡來的?顯然階乘的定義是從一個迴圈過程抽象來的,斐波那契數列的定義是個迭代的抽象。於是,我們先從一個本不是遞迴的事實抽象出一個遞迴的定義,然後我們說,“因為問題的定義是遞迴的,因此我們很容易寫出遞迴演算法”,接著說,“我們也能將這個遞迴演算法轉化為迴圈、迭代演算法”,給人的感覺就像是1÷3=0.33……,0.33……×3=0.99……,然後我們花了好大的心智才明白1=0.99……。

還是有那麼些人樂此不疲,是凡講到遞迴就要提到這兩個,結果,沒有一個學生看到階乘那樣定義沒有疑問的,沒有一個對於那個遞迴的階乘函式抱有欽佩之情的——瞎折騰什麼呢?所以,如果要講遞迴,就要一個令人信服的例子,而這個例子非漢諾塔莫屬。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-959473/,如需轉載,請註明出處,否則將追究法律責任。

相關文章