學習遞歸的目的,最終是運用遞歸解決問題。
了解遞歸的運作模型(https://zhuanlan.zhihu.com/p/166173378)之後,就能根據遞歸函數的靜态代碼推算執行結果了。實際上,對遞歸函數的執行結果的推算,可從另一個途徑進行,即運用類似于數學歸納法的思想。下面以計算階乘的遞歸函數為例說明。
考察遞歸函數int
fact(int n)1.它确實能正确計算0的階乘的值;
2.當執行函數fact(1)調用時,它傳回的值是1* fact(0),即1,是以1!計算正确。
3.當執行函數fact(2)調用時,它傳回的值是2* fact(1),隻要fact(1)能計算正确(上一步驟已證明正确),那麼fact(2)也能正确計算2!
……
4.當執行函數fact(n)調用時,它傳回的值是n*fact(n-1),從計算式子可推斷出,隻要fact(n-1)能計算出(n-1)!,那麼,fact(n)就能計算出n!
根據數學歸納法的思想,就推斷證明了fact(n)能計算出n!
切入正題——用遞歸思維程式設計解決問題
當描述問題解答的算法本身是遞歸式子時,我們很容易寫出程式的遞歸函數。
對于一般的問題,設計遞歸函數的步驟、方法如下:
1.首先要明确地描述問題、定義問題,
設計函數首部,用函數的參數刻畫待解決問題的數量特征及其關系本質,特别地,函數參數(即變量)描述問題的規模,并描述函數的功能,或者說要非常明确函數要完成的任務,對設計者來說,無論口頭和書面,都能清晰地、準确無誤地、一般化地描述。
2.遞歸函數函數體的程式架構:
确定最小問題,即确定遞歸邊界,直接解決最小問題;
當問題規模較大時,對問題進行分解,用遞歸調用解決分解出的小問題,用小問題的解構造出函數首部定義的原問題的解。
遞歸的三要素:定義描述問題;解決最小問題;分解大問題、用遞歸解決分解出的小問題。
用遞歸思維解決問題,我們不必關注如何處理及求解問題的詳細過程和步驟,你甚至可以不知道計算解題過程就把問題給程式設計解決了。豈不是美死了!!!就像孫悟空懂規則,但不知道具體如何玩漢諾塔,可他居然完成了移動漢諾塔的目标(https://zhuanlan.zhihu.com/p/168684834)。
在一定的時間、空間限制下,人的體力有限,思維力也有限,遞歸思維對實踐最有用的指導,就是把腦力集中于定義問題這個關鍵點上,不用去找解題的過程。定義(問題)即解決(問題),
定義即解決! 定義即解決!! 定義即解決!!!有點像具備先進飛彈的現代先進戰鬥機,它們具備“
發現即摧毀”的能力。
上面說的是不是讓人覺得雲裡霧裡,是不是吹牛。馬上給您醍醐灌頂。
例1輸入整數n,輸出1~n的整數
。例如n=5時,輸出12345。 這例子夠簡單。
問題:要解決的問題直截了當,即輸出1~n的整數,設計函數首部void prn1n(int n);确定并解決最小問題;定義函數void prn1n(int n)明确其任務後,“
定義即解決”指的是在設計函數體時,認為比1~n規模小的問題都已得到解決,都立即為我所用,即輸出1~n-1或1~n-2的問題等,立即用遞歸得到解決。
我們還可以這樣定義問題題:輸出s~t之間的整數,函數首部void prnst(int s,int t); 表示輸出s~t之間的整數;确定并解決最小問題;定義函數void prnst(int s,int t) 明确其任務後,“
定義即解決”指的是在設計函數體時,認為比s~t規模小的問題都已得到解決,都立即為我所用,例如輸出s+1~t、s+1~t-1或s~t-2或s~(s+t)/2等等,做法分别是prnst(s+1,t)、prnst(s+1,t-1)、prnst(s,t-2)、prnst(s ,(s+t)/2)。是以,要寫出輸出s~t之間的整數的遞歸函數,容易之極。
例1高明之處在于,簡單的輸出1~n之間的整數的問題,玩出如此之多的花樣,對于線性參數表示的問題,它的遞歸方式(對問題的切分方式)基本上類似于以上幾種方法之一。如數組的選擇、冒泡、快速、合并排序,一維的背包問題等等,概莫能外。 遞歸思維解題的另一個關鍵點是劃(切)分問題。 例2冒泡排序,全遞歸實作。定義函數:void bubbleSort(int a[],int n); 它的任務是對數組a的前n個元素排序。 按照“
定義即解決”原則,在設計函數體時,極易做到(用遞歸)排序數組a的前i(i<n)個元素。定義函數:void bubble(int a[],int n); 它的任務是對數組a的前n個元素進行冒泡處理,使a[n-1]最大。 按照“
定義即解決”原則,在設計函數體時,極易做到(用遞歸)對數組a的前i(i<n)個元素冒泡,使a[i-1]最大。基于此,設計遞歸函數實作冒泡排序不費吹灰之力。
#include
例3 合并排序。void mergeSort(int a[],int s,int t)與
例1方法5的遞歸函數相似度驚人!!
#include
例4 選擇排序,全遞歸實作
#include
研究比較
例1的各種遞歸方法,您很容易了解選擇排序、冒泡排序和合并排序以及其中的冒泡函數void bubble(int *a, int n)和函數void selectMin(int *a, int n)的遞歸實作。
趙馮平:遞歸思維——插入排序、快速排序的全遞歸實作
什麼是遞歸思維? 有請各位高見。
趙馮平:了解遞歸函數調用的最好模型(沒有之一)zhuanlan.zhihu.com
趙馮平:漢諾塔、遞歸, 為什麼孫悟空可以輕松玩轉?zhuanlan.zhihu.com