what:在計算機科學中是指一種通過重復將問題分解為同類的子問題而解決問題的方法
when:發現問題可以分解為同類子問題且采用同樣的方式去解決
how:找到遞歸出口和遞歸體
步驟:通過分析題目是否可以分解為若干重復子問題,判斷是否可以采用遞歸算法進行解決。確定采用遞歸算法之后,開始找遞歸出口和遞歸體,這是遞歸算法的核心部分,下面通過兩個題目講一下我的解題步驟。
P1:leetcode226 翻轉二叉樹 簡單題
解答:題目很簡單,翻轉二叉樹,然后查看輸入輸出,其實就是將每個節點的左右子樹進行了交換。這個時候子問題已經出現了,交換左右子樹,按題目說法就是翻轉左右子樹,可以采用遞歸來做。
接下來需要找到遞歸出口和遞歸體,一般情況下先找遞歸體,因為確定遞歸的時候大概就已經確定了遞歸體,然后就是考慮邊界問題,確定遞歸出口。
回到題目,根據題目的分析,可以確定遞歸體即為交換翻轉當前節點的左右子樹,當遍歷至根節點無節點可翻轉時即退出遞歸,即找到遞歸出口當前節點為null。
接下來代碼實現:
public class Solution{ /** 226. 翻轉二叉樹 簡單題 * */ public TreeNode invertTree(TreeNode root) { this.invert(root); return root; } private void invert(TreeNode root){ if (null == root) { return; } TreeNode left = root.left; TreeNode right = root.right; root.right = invertTree(left); root.left = invertTree(right); } } class TreeNode { int val; TreeNode left; TreeNode right; TreeNode() { } TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
P2:leetcode24,給定一個鏈表,兩兩交換其中相鄰的節點,並返回交換后的鏈表。你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。中等題
解答:這個題目也可以采用遞歸來做(但是綜合考慮的話,遞歸其實不是最好的方法,這里只是用來舉例說明一下遞歸該怎么去實現)。同樣的找遞歸體遞歸出口,遞歸出口很簡單,鏈表遍歷完,遞歸就結束了,但是需要考慮,子問題是需要兩兩交換,所以遞歸應該滿足兩個條件,當前節點不能為空以及當前節點的下個節點不能為空。而遞歸體,觀察我們需要完成的問題,將相鄰兩個節點交換,那么我們一直重復做的事情就是在交換兩個相鄰節點以及將交換后的節點正確鏈接到下個節點,遞歸體也找到了,接下來實現。(遞歸體實現的時候對鏈表的考察,這里注意好賦值順序一般問題不大,最好的就是畫圖,然后就是遞歸體之間的鏈接需要考慮到,如果把遞歸體當做一個整體的話,當前塊要鏈接到下一塊)
接下來代碼實現:
/** * 24. 兩兩交換鏈表中的節點 */ public ListNode swapPairs(ListNode head) { //1.出口 if (null == head || null == head.next) { return head; } //2.找到遞歸體 //兩兩交換 //下次遞歸頭結點 ListNode nextHead = head.next.next; //相鄰節點交換 ListNode temp = head.next; head.next = nextHead; temp.next = head; //重置頭結點 head = temp; //設置當前組指向下一個頭結點 head.next.next = swapPairs(nextHead); return head; }
最后:遞歸應該算是最簡單的算法,一般入門就是遞歸,容易想到,實現簡單。但是並不是能夠采用遞歸算法,遞歸算法就是最優的選擇,在選擇算法時候,需要根據應用場景進行選擇,因為遞歸需要遞歸棧保存現場信息,帶來額外的空間消耗。工作中最常使用遞歸的應該就是構造部門樹了,數據量小,時間空間都不會占用太多,遞歸實現很方便。
下集預告:貪心算法
(可掃描我的微信公眾號二維碼關注我的公眾號,更快獲取更新哦)