☀️~爆肝万字总结递归~❤️玩转算法系列之我如何才能掌握递归解题的能力❤️~十大经典问题助你突破极限~☀️

Posted Roninaxious

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了☀️~爆肝万字总结递归~❤️玩转算法系列之我如何才能掌握递归解题的能力❤️~十大经典问题助你突破极限~☀️相关的知识,希望对你有一定的参考价值。

🍅 作者主页:Roninaxious
🍅 欢迎点赞 👍 收藏 ⭐留言 📝

🚢前言

🎐何为递归

递归顾名思义就是´递´´归´

👀所谓的‘递’也就是“向下递去”,这个问题可以分解为若干个且形式相同的子问题,这些子问题可以使用相同的思路来解决。

👀所谓的‘归’也就是“归来的意思”,什么时候归来?所以涉及到了临界点,也就是‘递’中的分解终止条件。


🎐递归的工作原理

💦根据递归两字的含义,不难发现,它包含了递去和归来两个层面。


💦根据以上分析可以发现这个流程和栈的工作原理一致,递归调用就是通过栈这种数据结构完成的。整个过程实际上就是一个栈的入栈和出栈问题。然而我们并不需要关心这个栈的实现,这个过程是由系统来完成的。

🎐递归 == 数学归纳法 ?

众所周知,计算机是数学的一个分支。这是一个不可否认的问题,在编程过程中,我们总能在数学中找到答案。递归提高了代码的可读性,但同时加大了内存的消耗,递归让人又爱又恨;不可否认,递归很重要!那么它和数学归纳法有什么联系呢?

💦归纳法
对于一个命题S(n),n为大于0的自然数;如何证明命题成立呢?

👀1.当n=1时,命题成立
👀2.当n=k时,命题成立
👀3.证明n=k+1时命题也成立

🧧递归本质上是类似归纳法,但又有点稍微不同🧧

递归是从F(n)倒推到F(1),然后在F(1)回溯到F(n);相比于归纳法多了一步。


🚢如何正确使用递归

使用递归需要注意很多点,否者造成无限递归⛓就是在造孽了

🎐递归常用流程

 *	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 {     //如果下右上左都不能走,所以此路不同,置为3
                    arr[x][y] = 3;
                    return false;
                }
            } else {  //arr[x][y] != 0    ? 有可能是1,2,3
                return 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);
//    }
}

🚢经典问题二之链表反转三(反转中间部分)

飞速总结中…

以上是关于☀️~爆肝万字总结递归~❤️玩转算法系列之我如何才能掌握递归解题的能力❤️~十大经典问题助你突破极限~☀️的主要内容,如果未能解决你的问题,请参考以下文章

❤️爆肝万字!一文最全总结之Spring从入门到入土❤️(建议收藏)

❤️爆肝万字!一文最全总结之Spring从入门到入土❤️(建议收藏)

❤️爆肝万字整理的综合架构web服务之nginx详解❤️,附建议收藏

Python OpenCV实战画图——这次一定能行!爆肝万字,建议点赞收藏~❤️❤️❤️

❤️大学三年沉淀,把我的学习经验分享给你,爆肝万字带你走进编程世界!❤️

(大厂必备)厂长熬夜爆肝万字之多线程高并发JUC编程⭐学妹已收藏