教你如何寫遞歸(數學概括法,乾貨強推!)

引言

對於不少程序員來講,寫遞歸程序是比較頭疼的一件事,即便是把程序看懂了,輪到本身寫的時候也是一臉懵逼,那麼到底寫遞歸有沒有方法論呢?固然!本文就將從數學概括法的角度教你如何寫遞歸程序。 
程序員

數學概括法與遞歸的關係

有人會疑惑,不是說好寫遞歸嘛,怎麼扯到數學概括法了?別急慢慢往下看你就知道了。 
  
首先咱們來溫習一下數學概括法的定義:函數

數學概括法:用於證實斷言對全部天然數成立。spa

證實過程:指針

  • 證實對於N=1成立
  • 證實N>1時:假設對於N-1成立,那麼對於N成立

  
咋一看你或許有點蒙,沒事,咱們來舉個例子你就差很少懂了。code

Alt text

  
怎麼樣,總結一下就是:先證實第一個天然數成立,而後假設對於n-1成立,在用這個假設去推導證實對於n也成立遞歸

對於天然數的第一個數字到底是0仍是1,是一個學術界至今沒有統一的問題,咱們這裏使用1做爲天然數的第一個數。ip

  
接下來重點來了!!!咱們來看看如何用遞歸寫上面那個例子get

Alt text 
  
是否是驚人的類似啊!原來遞歸的思路和數學概括法的證實思路是同樣的啊! 
  
鋪墊了這麼多,終於引出咱們今天要講的遞歸書寫方法論。 
數學

遞歸書寫方法

1. 先通常,後特殊(邊界)

先寫遞歸的主體部分,再回頭寫邊界(一行行讀主體部分的代碼,尋找特殊的邊界狀況)it

2. 每次調用必須縮小問題規模

3. 每次問題規模縮小程度必須爲1(不要貪心)

這裏的1並非狹義的,比方說若是二分查找的時候,每次縮小一半的規模也是能夠的

  
上面四點很是重要,爲了幫助你們理解,接下來我舉幾個例子讓你們熱熱身。 

例1:建立鏈表

咱們想要建立一個五節點的單向鏈表(以下圖所示):

Alt text 
  
該怎麼作呢?首先咱們先假設若是已經獲得了後四個節點的鏈表

Alt text 
  
那麼此時咱們只須要將第一個節點的next指針指向(n-1)結果的頭節點便可

Alt text 
代碼以下:

 
public ListNode createLinkedList(List<Integer> data) {
ListNode firstNode = new Node(data.get( 0));
// 將第一個節點的next指針指向n-1已經建立好鏈表的頭節點
firstNode.next = createLinkedList(data.subList( 1, data.size()));
return firstNode; // 返回頭節點
}

  
此時咱們先通常的步驟就完成了,看看是否是每次調用函數都縮小問題規模了,是否是問題規模每次都縮小1,都知足條件!接下來就是後特殊了,劃重點:一行行查看此時的代碼,想一想每一行代碼在什麼狀況下出異常,一旦發現這就是特殊狀況了! 
  
例如:上面函數中的第一行,若是data爲空的話,是否是就報錯了,那麼此時就須要作特殊狀況處理:

 
public ListNode createLinkedList(List<Integer> data) {
if (data.isEmpty())
return null;
ListNode firstNode = new Node(data.get( 0));
// 將第一個節點的next指針指向n-1已經建立好鏈表的頭節點
firstNode.next = createLinkedList(data.subList( 1, data.size()));
return firstNode; // 返回頭節點
}

  
那麼到此爲止,咱們就完成了建立鏈表的遞歸程序編寫。不過癮?那咱們再來一個!

例2:反轉鏈表

Alt text 
  
仍是老樣子,咱們先假設n-1個鏈表已經反轉完成了

Alt text 
  
那麼此時咱們應該怎麼作呢?此時1這個元素的next指針仍是指向2的,由於後面的反轉並不會影響1元素的next指針:

Alt text 
  
因此此時咱們只須要將節點2的next設爲1,將節點1的next設爲null便可完成反轉!

Alt text 
代碼以下:

 
public ListNode ReverseList(ListNode head) {
// 這裏獲得n-1反轉後鏈表的頭節點,也就是反轉前的最後一個節點
ListNode newHead = ReverseList(head.next);
// 將節點2的next設爲節點1
head.next.next = head;
// 將節點1的next設爲null
head.next = null;
return newHead;
}

  
那麼此時先通常就完成了,接下來就是尋找特殊(邊界)狀況了,一行行代碼看下來發現head和head.next爲null時會報異常,因而處理異常狀況: 

 
public ListNode ReverseList(ListNode head) {
if(head == null || head.next == null)
return head;
ListNode newHead = ReverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}

  
到此爲止,反轉鏈表函數就已經完成,是否是感受按照數學概括法的思路寫遞歸仍是蠻清晰的呢!

總結

數學概括法是遞歸的依據。

書寫遞歸的思路跟數學概括法的證實過程類似,不過須要注意的是,先書寫通常狀況,後根據通常狀況考慮特殊狀況;同時要牢記遞歸函數每次縮小規模程度必須爲「1」!

方法已經教給你了,接下去就是多練習了,趕忙找點題目練練手吧!

相關文章
相關標籤/搜索