【☀️~爆肝万字总结递归~❤️玩转算法系列之我如何才能掌握递归解题的能力❤️~十大经典问题助你突破极限~建议收藏☀️】
🍅 作者主頁:Roninaxious
🍅 歡迎點贊 👍 收藏 ?留言 📝
🚢前言
🎐何為遞歸
遞歸顧名思義就是′遞′和′歸′
👀所謂的‘遞’也就是“向下遞去”,這個問題可以分解為若干個且形式相同的子問題,這些子問題可以使用相同的思路來解決。
👀所謂的‘歸’也就是“歸來的意思”,什么時候歸來?所以涉及到了臨界點,也就是‘遞’中的分解終止條件。
🎐遞歸的工作原理
💦根據遞歸兩字的含義,不難發現,它包含了遞去和歸來兩個層面。
💦根據以上分析可以發現這個流程和棧的工作原理一致,遞歸調用就是通過棧這種數據結構完成的。整個過程實際上就是一個棧的入棧和出棧問題。然而我們并不需要關心這個棧的實現,這個過程是由系統來完成的。
🎐遞歸 == 數學歸納法 ?
眾所周知,計算機是數學的一個分支。這是一個不可否認的問題,在編程過程中,我們總能在數學中找到答案。遞歸提高了代碼的可讀性,但同時加大了內存的消耗,遞歸讓人又愛又恨;不可否認,遞歸很重要!那么它和數學歸納法有什么聯系呢?
💦歸納法
對于一個命題S(n),n為大于0的自然數;如何證明命題成立呢?
👀1.當n=1時,命題成立
👀2.當n=k時,命題成立
👀3.證明n=k+1時命題也成立
🧧遞歸本質上是類似歸納法,但又有點稍微不同🧧
| 遞歸是從F(n)倒推到F(1),然后在F(1)回溯到F(n);相比于歸納法多了一步。 |
💟 對于遞歸你了解多少了呢?💟
- 🚢前言
- 🎐何為遞歸
- 🎐遞歸的工作原理
- 🎐遞歸 == 數學歸納法 ?
- 🚢如何正確使用遞歸
- 🎐遞歸常用流程
- 🎐遞歸的三大要素
- 💥1、縮小問題規模
- 💥2、明確遞歸終止條件
- 💥3、給出遞歸終止時的處理辦法
- 🚢經典問題一之迷宮求解
- 🎐迷宮求解流程分析
- 🎐源代碼
- 🚢經典問題二之鏈表反轉一(全反轉)
- 🎐鏈表反轉流程分析
- 🎐源代碼
- 🚢經典問題二之鏈表反轉二(反轉前n個節點)
- 🎐鏈表反轉2流程分析
- 🎐源代碼
- 🚢經典問題二之鏈表反轉三(反轉中間部分)
🚢如何正確使用遞歸
使用遞歸需要注意很多點,否者造成無限遞歸?就是在造孽了
🎐遞歸常用流程
* void func(mode)* {* if(endCondition) //臨界條件* {* constExpression //處理方法* }* else* {* accumrateExpreesion //歸納項* mode=expression //步進表達式* func(mode) //調用本身,遞歸* }* }🙄注意一定不要在自己腦子里遞來遞去的,你的腦子能壓幾個棧呢,否者很容易就陷入死腦筋之中🙄
🎐遞歸的三大要素
💥1、縮小問題規模
👀這個縮小問題規模可以根據歸納法先進行回推,比如讓求S(n),我們可以先求當n=1的解決方法,再求n=2時的解決方法…一直歸納到S(n);這樣我們就可以推導出關系式S(n)=S(n-1)+?
| 稍后會根據經典問題思考如何縮小問題規模。 |
💥2、明確遞歸終止條件
👀遞歸既然有去有回,就必須有一個臨界條件,當程序到達這個臨界值就必須進行歸來;否者就肯定會造成無線遞歸下去,什么棧也扛不住!
💥3、給出遞歸終止時的處理辦法
👀根據上文中闡述的遞歸的工作原理,遞歸是通過棧來實現的;當程序到達臨界條件時,后續必須進行出棧。了解函數的壓棧和出棧過程,函數出棧也就是返回到上一個函數的調用處繼續執行。例如求解F(n),遞推式是F(n)=F(n-1)+F(n-2),終止條件F(1)=1,F(0)=0;它的流程是每次進行壓棧F(n-1)、F(n-2),當達到終止條件時進行函數的出棧,那么返回什么值呢?根據遞歸的倒數第一步F(2)=F(1)+F(0)所以返回的是F(1)+F(0),也就是1.
🚢經典問題一之迷宮求解
?問題描述?
- 找到迷宮路出口
🎐迷宮求解流程分析
右下角為迷宮的出口,方塊為墻,圓形塊為迷宮入口;如何找到一條通路從入口到出口?
🎈為了表示方便,我以二位數組為例進行操作。擬定二位數組中的1代表墻,2代表通路,3代表路已經走過且不通,0代表未走過的路。
?? 初始化迷宮
📑首先迷宮有四個方向可以走(上下左右),所以我們應該先自定義一個策略,規定?下右上左?(優先走下,然后右->上->左),也就是當下不通時,走右…
🎆根據遞歸三要素
| 1.分解成子問題 |
| 2.遞歸終止條件 |
這個當然就是出口了,也就是map[5][6] == 2;
🎐源代碼
public class Labyrinth {private static final int X_MAX = 7;private static final int Y_MAX = 8;private static int[][] map = new int[X_MAX][Y_MAX];/*** 迷宮問題--遞歸* 思路:* 策略:規定下右上左為行走策略* 1代表障礙物,2代表走過的路,3代表路已經走過且不通,0代表未走過的路*/public static boolean setWays(int[][] arr, int x, int y) {if (arr[5][6] == 2) { //終止條件return true;} else {if (arr[x][y] == 0) {arr[x][y] = 2;if (setWays(arr, x, y + 1)) { //下return true;} else if (setWays(arr, x + 1, y)) { //右return true;} else if (setWays(arr, x, y - 1)) { //上return true;} else if (setWays(arr, x - 1, y - 1)) {//左return true;} else { //如果下右上左都不能走,所以此路不同,置為3arr[x][y] = 3;return false;}} else { //arr[x][y] != 0 ? 有可能是1,2,3return false;}}}//測試public static void main(String[] args) {for (int i = 0; i < X_MAX; i++) {map[i][0] = 1;map[i][Y_MAX - 1] = 1;}for (int j = 1; j < Y_MAX - 1; j++) {map[0][j] = 1;map[X_MAX - 1][j] = 1;}map[1][2] = 1;map[1][3] = 1;map[2][3] = 1;map[3][3] = 1;map[3][2] = 1;setWays(map, 1, 1);printMap(map);}//打印迷宮地圖public static void printMap(int[][] arr) {for (int j = 0; j < Y_MAX; j++) {for (int i = 0; i < X_MAX; i++) {System.out.print(arr[i][j] + " ");}System.out.println();}} }?? 運行結果
🚢經典問題二之鏈表反轉一(全反轉)
🔰問題描述🔰
- 給定一個鏈表,將其反轉
- 例如:1->2->3->4 ---- 4->3->2->1
🎐鏈表反轉流程分析
📑對于該問題,將其整體分為兩部分1和n-1,將其反轉即可;然后再將n-1部分整體分為兩部分,再將其反轉即可。
🎆根據遞歸三要素
| 1.分解成子問題 |
| 2.遞歸終止條件 |
對于這個終止條件,這個應該比較簡單,當遞歸到最后一個節點時開始進行歸來操縱,也就是head.next == null || head == null;對于這個head == null是因為空鏈表的情況。
| 3.遞歸終止處理辦法 |
由于我們最后反轉鏈表之后,要將最后一個節點定義為頭節點。所以需要將該節點返回。
if (head == null || head.next == null) {return head;}🎐源代碼
package com.zsh.algorithm.recursion;/*** @author:Ronin* @since:2021/9/16* @email:1817937322@qq.com*//*** 反轉單鏈表 question one* eg:1->2->3->4 ---- 4->3->2->1*/ public class ReverseLinkedList {/*** 鏈表反轉* 思路:將鏈表分成兩部分(1.第一個頭節點 2.頭節點之后的所有)* 遞歸回溯時將指針反轉即可* 遞歸出口條件:head || head.next == null* @param head* @return*/static Node reverse(Node head) {if (head == null || head.next == null) {return head;}Node currentNode = reverse(head.next);head.next.next = head;head.next = null;return currentNode;}// public static void main(String[] args) { // Node node = new Node(1); // Node node2 = new Node(2); // Node node3 = new Node(3); // LinkedListNode linkedListNode = new LinkedListNode(); // linkedListNode.add(node); // linkedListNode.add(node2); // linkedListNode.add(node3); // Node reverseHead = reverse(linkedListNode.head); // linkedListNode.print(reverseHead); // } }//class Node { // int num; // Node next; // public Node() {} // public Node(int i) { // this.num = i; // } //} //class LinkedListNode { // Node head = null; // // public void add(Node addNode) { // if (head == null) { // head = addNode; // return; // } // Node temp = head; // while (temp.next != null) { // temp = temp.next; // } // temp.next = addNode; // temp = addNode; // temp.next = null; // } // // public void print(Node reverseHead) { // Node temp = reverseHead; // while (temp != null) { // System.out.print(temp.num); // System.out.print("->"); // temp = temp.next; // } // } //}
🚢經典問題二之鏈表反轉二(反轉前n個節點)
🔰問題描述🔰
- 反轉單鏈表2
- 反轉前n個節點 1->2->3->4->5->6
- reverse(node head, 3)
- 3->2->1->4->5->6
🎐鏈表反轉2流程分析
如果已經了解了全部節點的鏈表反轉,那么這個應該也比較簡單;反轉前n個節點,當然就是臨界條件改變了。全部節點的反轉是在最后一個節點作為臨界值,而這個只需要加入一個新的變量n進行控制前n個節點反轉即可。
下圖是出棧得流程
🎐源代碼
public class ReverseLinkedListFront {static Node cur = null;static Node reverse(Node head, int n) {if (n <= 1 || head.next == null) {cur = head.next;return head;}Node lastNode = reverse(head.next, n-1);head.next.next = head;head.next = cur;return lastNode;}// public static void main(String[] args) { // Node node = new Node(1); // Node node2 = new Node(2); // Node node3 = new Node(3); // LinkedListNode linkedListNode = new LinkedListNode(); // linkedListNode.add(node); // linkedListNode.add(node2); // linkedListNode.add(node3); // Node reverseHead = reverse(linkedListNode.head, 3); // linkedListNode.print(reverseHead); // } }🚢經典問題二之鏈表反轉三(反轉中間部分)
對于1->2->3->4->5,我們將m=2,n=4進行反轉
結果1->4->3->2->5
思路:
1.對于反轉(m~n),如果m=1也就是前n個節點反轉。
2.frontHead.next = reverse(frontHead.next, m-1, n-1);這一行代碼是最難理解的。m-1代表每次frontHead.next,當m=1時,就到達了鏈表反轉部分的起點;n-1是因為(例如m=2,n=5,只反轉n-m=3個節點)。
3.return reverse(frontHead, n);會將中間(m~n)個節點進行反轉
4.在第三步遞歸結束之后會返回到這一步frontHead.next =reverse(frontHead.next, m-1, n-1); 開始從m=3進行回溯,直到m=1.回溯時進行連接
5.該題非常考驗遞歸解題能力.
飛速總結中…
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的【☀️~爆肝万字总结递归~❤️玩转算法系列之我如何才能掌握递归解题的能力❤️~十大经典问题助你突破极限~建议收藏☀️】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【IDEA忽略文件Settings设置】
- 下一篇: 【算法系列之万字总结常用的查找算法,持续