递归理解一:递归递推函数的理解和读取详解

目录

参考资料:

基础知识:

1. 多项并列递归的执行流程

1). 最重要的:

 2).   解释

简单问题分析

1).  简单程序举例

 3. 总结

 并列问题分析:

1. 问题引申

2.逻辑图演示

3. 上述分析总结(理论篇)

4. 一些特别的处理方式

1. n项递归、递推函数的读取/理解

2. 递归、递推函数理解举例(数学关系)

3.递归、递推函数理解举例(非数学关系:&|!)

4. 方法的执行顺序,上述分析


参考资料:

递归递推的基本理解https://blog.csdn.net/qq_23095607/article/details/125245890

通项公式的部分求解方法https://www.bilibili.com/video/BV1E44y1o7x8/?spm_id_from=333.337.search-card.all.click&vd_source=97411b9a8288d7869f5363f72b0d7613


基础知识:

1. 多项并列递归的执行流程

1). 最重要的:

  • 多项并列递归的执行流程可以概述为:

传递至底 → 反弹后推

  • 画图如下:

 2).   解释

  • 下面将 结合两个并列的递归对上面所画图进行解释 

  •  ①②③④⑤递归函数传递至底,触发终止条件;
  • ⑥⑦消息回弹一级,执行后续所有操作,直到遇到下一个递归函数;
  • ⑧⑨⑩11递归函数请求传递至底,触发终止条件;
  • 12,13消息回弹一级,执行后续所有操作,直到遇到下一个递归函数;
  • 14,15递归函数请求传递至底,触发终止条件;
  • 16,17,18,19,20,21消息回弹一级,执行后续操作,无递归函数,则进行返回并后推操作,直到遇到递归函数或者返回到根节点;
  • 22,23,24,25递归函数传递至底,触发终止条件;
  • 26,27消息回弹一级,执行后续所有操作,直到遇到下一个递归函数;
  • 28,29递归函数请求传递至底,触发终止条件;
  • 30,31,32,33消息回弹一级,执行后续操作,无递归函数,则进行返回并后推操作,直到遇到递归函数或者返回到根节点;

  • 上述模型可以抽象为:

  •  按照,并列递归,并列前,并列中,并列后三个位置操作的分类,如图他们的执行顺序为:
  • 递归到底,执行并列前所有操作,最末端的仅执行终止条件前操作,按照代码,顺序为,由上至下,由浅入深,如上图中:{1,2,3,4,5},{7,8,9},{11},{15,16,17},{19}
  • 最后一次反弹回退,执行并列后所有操作,按照代码,执行顺序为:由上而下,由深而浅,如上图中:{12,13,14},{20,21}
  • 当前一个递归函数,及它深层的操作执行完毕后,就会执行并列中的操作。

  • 上述模型还可以进一步抽象成

  •  进而抽象出执行顺序的概念模型

  • 其中,递归至底,指的是最近的未被执行的一个递归函数,直至终止条件
  • 反弹后推:反弹指的是触发终止条件,进行返回,后推指的是反弹后执行后面的程序;

简单问题分析

1).  简单程序举例

    @Test
    public void test_02(){
        // 进行n到0  的遍历
        serachNum(3);
    }

    private  void serachNum(int num){
        // 判断前输出
        System.out.println("判断前操作:"+num);
        if(num<0){
            System.out.println("该数值小于0");
            return;
        }
        //进行递归前操作
        System.out.println("递归前操作"+num);
        int num_1 = num-1;
        serachNum(num_1);
        //递归后操作
        System.out.println("递归后操作"+num);
    }
  • 遍历从n到0,例如遍历从3到0,那么这个递归函数的结构可以解析为

  

  •  首先终止条件即为反弹条件,有且只能满足一次,最大值为-1
  • 递归函数即为递推到底的缺口
  • 累计条件即为每次向下递推的数据更新

那么第一次向下递推到底的输出为:

递归函数前,由上至下执行操作,直到触底

 触底后进行反弹,由下至上,执行递归函数的后续操作

 那么这个函数的输出为:

判断前操作:3
递归前操作3
判断前操作:2
递归前操作2
判断前操作:1
递归前操作1
判断前操作:0
递归前操作0
判断前操作:-1
该数值小于0
递归后操作0
递归后操作1
递归后操作2
递归后操作3

 那么画图可以表示为:

 3. 总结

  • 递推函数的规律为:
  • 通过对自身不断调用,由外而内,直至触底(即有且只满足一次终止条件)
  • 然后反弹回上一层,调用后面的方法

 并列问题分析

1. 问题引申

  • 通过对以上单项递归进行分析,进而引申出多项递归的分析,以快速排序为例,关于快排的概念,可以参考视频
  • 源码(可以不看,看下面的分析)
package com.example.demo.controller;


import java.util.Arrays;

/**
 * @program: demoForWeb
 * @description: 快排
 * @author: wjl
 * @create: 2023-02-14 00:47
 **/
public class QuickSort {
    public static void sort(int array[], int left, int right) {
        System.out.println("        判断前:left:"+left+"======right:"+right+"======array:"+ Arrays.toString(array));
        int i, j;
        int pivot;
        if (left >= right) {
            System.out.println("        判断中:left:" + left + "======right:" + right+"======array:"+ Arrays.toString(array));
            return;
        }
        System.out.println("    判断后操作前:left:"+left+"======right:"+right+"======array:"+ Arrays.toString(array));
        i = left;
        j = right;
        pivot = array[i];
        while (i < j) {
            while (i < j && pivot <= array[j])
                j--;
            if (i < j)
                array[i++] = array[j];
            while (i < j && pivot > array[i])
                i++;
            if (i < j)
                array[j--] = array[i];
        }
        array[i] = pivot;
        System.out.println("    操作后迭代前:left:"+left+"======right:"+right+"======array:"+ Arrays.toString(array));
        sort(array, left, i - 1);
        System.out.println("迭代①后迭代②前:left:"+left+"======right:"+right+"======array:"+ Arrays.toString(array));
        sort(array, i + 1, right);
        System.out.println("      迭代②后:left:"+left+"======right:"+right+"======array:"+ Arrays.toString(array));
    }
    public static void quickSort(int array[]) {
        sort(array, 0, array.length - 1);
    }
    public static void main(String[] args) {
        int a[] = { 5, 7, 4, 8, 6, 1 };
        quickSort(a);
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + " ");
        }
    }
}
  • 根据上面提到的视频,以及对代码的分析,可以将快排分为三部分

  • ①终止条件,即反弹条件,有且只能满足一次,即满足left == right就返回
  • ②递推参数的处理,取数组中第一个元素,然后把小于它的元素放于左边,大于它的元素放于右边,如下:

  • ③将左半部分进行递归;
  • ④将右半部分进行递归;
  • ⑤第一次递归前的操作;
  • ⑥第一次递归后,第二次递归前的操作;
  • ⑦第二次递归后的操作;

2.逻辑图演示

1)基本流程分析

 以数组【5,7,4,8,6,1】为例:

  • 输入数组[5, 7, 4, 8, 6, 1],以及左右两个下标{left = 0,right = 5}
  • 选取下标为0,数值为5做参考,左边小于,右边大于:[1, 4, 5, 8, 6, 7]
  • 入参[1, 4, 5, 8, 6, 7],两个坐标{left = 0,right = 1}
  • 选取下标为0,数值为1做参考,左边小于,右边大于:[1, 4, 5, 8, 6, 7]
  • 入参[1, 4, 5, 8, 6, 7],两个坐标{left = 0,right = -1},因为left > right,满足终止条件,所以进入③递归函数后触发反弹后推,到④
  • 入参[1, 4, 5, 8, 6, 7],两个坐标{left = 1,right = 1},因为left = right,满足终止条件,所以进入④递归函数后触发反弹后推,然后执行一系列操作后返回,直到②
  • 入参[1, 4, 5, 8, 6, 7],两个坐标{left = 3,right = 5}
  • 选取下标为3,数值为8做参考,左边小于,右边大于:[1, 4, 5, 7, 6, 8]
  • 入参[1, 4, 5, 7, 6, 8],两个坐标{left = 3,right = 4}
  • 选取下标为3,数值为7做参考,左边小于,右边大于:[1, 4, 5, 6, 7, 8]
  • 入参[1, 4, 5, 6, 7, 8],两个坐标{left = 3,right = 3},因为left = right,满足终止条件,所以进入⑦递归函数后触发反弹后推,到⑧
  • 入参[1, 4, 5, 6, 7, 8],两个坐标{left = 5,right = 4},因为left > right,满足终止条件,所以进入⑧递推函数后触发反弹后推,然后执行一系列操作后返回,直到⑥
  • 入参[1, 4, 5, 6, 7, 8],两个坐标{left = 6,right = 5},因为left>right,满足终止条件,所以进入⑥递归函数后触发反弹后推,然后执行一系列操作后,直到终点;
  • 这就是整个分析过程。

2)分析注意事项

  • 对于上述,入参以及坐标建议不要深究(深究可以看代码),这里仅仅是为了说明递归多并列项,各个部分的执行顺序
  • 下面,你只需要注意:上图中箭头流程各个递推函数入参,以及各个部分的输出语句,来进行多重并列递归的执行顺序

A. 首先在①之前,输出为:

              判断前:left:0======right:5======array:[5, 7, 4, 8, 6, 1]
    判断后操作前:left:0======right:5======array:[5, 7, 4, 8, 6, 1]
    操作后迭代前:left:0======right:5======array:[1, 4, 5, 8, 6, 7]

B.进入①到达③之前,输出为:

              判断前:left:0======right:1======array:[1, 4, 5, 8, 6, 7]
    判断后操作前:left:0======right:1======array:[1, 4, 5, 8, 6, 7]
    操作后迭代前:left:0======right:1======array:[1, 4, 5, 8, 6, 7]

C. 进入③,反弹前,输出为:

        判断前:left:0======right:-1======array:[1, 4, 5, 8, 6, 7]
        判断中:left:0======right:-1======array:[1, 4, 5, 8, 6, 7]

D. 反弹后,进入④前,输出为:

迭代①后迭代②前:left:0======right:1======array:[1, 4, 5, 8, 6, 7]

E. 进入④,反弹前,输出为:

        判断前:left:1======right:1======array:[1, 4, 5, 8, 6, 7]
        判断中:left:1======right:1======array:[1, 4, 5, 8, 6, 7]

F. 反弹后,进入②前,输出为:

迭代②后:left:0======right:1======array:[1, 4, 5, 8, 6, 7]
迭代①后迭代②前:left:0======right:5======array:[1, 4, 5, 8, 6, 7]

 G. 以此类推,后续结果为:

②~⑤
         判断前:left:3======right:5======array:[1, 4, 5, 8, 6, 7]
    判断后操作前:left:3======right:5======array:[1, 4, 5, 8, 6, 7]
    操作后迭代前:left:3======right:5======array:[1, 4, 5, 7, 6, 8]

⑤~⑦
         判断前:left:3======right:4======array:[1, 4, 5, 7, 6, 8]
    判断后操作前:left:3======right:4======array:[1, 4, 5, 7, 6, 8]
    操作后迭代前:left:3======right:4======array:[1, 4, 5, 6, 7, 8]

⑦~反弹
        判断前:left:3======right:3======array:[1, 4, 5, 6, 7, 8]
        判断中:left:3======right:3======array:[1, 4, 5, 6, 7, 8]

反弹~⑧
迭代①后迭代②前:left:3======right:4======array:[1, 4, 5, 6, 7, 8]

⑧~反弹
        判断前:left:5======right:4======array:[1, 4, 5, 6, 7, 8]
        判断中:left:5======right:4======array:[1, 4, 5, 6, 7, 8]

反弹~⑥
      迭代②后:left:3======right:4======array:[1, 4, 5, 6, 7, 8]
迭代①后迭代②前:left:3======right:5======array:[1, 4, 5, 6, 7, 8]

⑥~反弹
        判断前:left:6======right:5======array:[1, 4, 5, 6, 7, 8]
        判断中:left:6======right:5======array:[1, 4, 5, 6, 7, 8]

反弹~结束
      迭代②后:left:3======right:5======array:[1, 4, 5, 6, 7, 8]
      迭代②后:left:0======right:5======array:[1, 4, 5, 6, 7, 8]

3. 上述分析总结(理论篇)

  • 所以,递归、递推函数可以的比喻为一个小球,自右向左,经过一个满是孔洞的地面;

  • 最上面孔洞最大,孔洞的大小随孔洞的深度递减;

  • 大孔洞下面还有小孔洞;

  • 触底反弹到离它最近的下一个孔洞,直至至最高层;

  •  那么这个过程可以概括为:递推至底、反弹后推的不断循环

  • 通过对自身不断地调用,将数据推至边界条件,直至返回;
  • 返回后又去寻找下一个自身的方法,继续推至边界条件,如此循环往复,直至遍历完所有;
  • 在努力寻找自身方法的路途中,顺便执行了路途中所有的数据操作;

4. 一些特别的处理方式

  •  首先是数据的累计递归函数,高中数学不好的建议学习通项公式,以及重点特征根法
  • 下面以斐波那契数列为例,来说明累计式递归函数,对于任一值的结论,如果对斐波那契数列还不熟,或者斐波那契数列的java代码可以参考
  • 斐波那契数列的递推式为:

F(n)=F(n -1)+ F(n -2)(n ≥ 2,n ∈ N*) ;

F(1)= 1;

F(0)=1

  • 第二种就是非累计式的,各个数据操作的执行情况 ,以上述快排为例
  • 我们可以把,递归、递推函数分为判断前,判断判断后递归n后,这几部分

  • 那么我们就可以按照输入值【5,7,4,8,6,1】 ,写出并关系的递归公式

  • 我们通过各个项式的累加,就可以得到各个阶段的数据操作; 

综合总结(实践篇):

1. n项递归、递推函数的读取/理解

  • 递归、递推函数的读其实是写的逆向运算,写的方法见文章
  • 首先明确终止条件数据操作部分递归递推部分(注意:终止条件在每一次递推到底过程中有且只满足一次)
  • 然后写出他们的递推公式F(),公式中的部分可以是数学关系(加减乘除等)或这是与或非关系
  • 根据公式就能看出递归、递推方法的含义;

2. 递归、递推函数理解举例(数学关系)

  • 数学关系递归函数举例:斐波那契数列java
    @Test
    public void A(){
        System.out.println("前100个斐波那契数列和为:" + function(100));
    }
 
    public int function(int num){
 
        if(num == 1||num == 2){
            return 1;
 
        }else if(num > 2){
            return function(num-1)+function(num-2);
 
        }else{
            System.out.println("error");
            return 0;
        }
    }
  • 结构分析 

  •  所以我们根据终止条件和递归条件,得到下面关系式

F(n)=F(n -1)+ F(n -2)(n ≥ 2,n ∈ N*) ;

F(2)= 1;

F(1)=1

  • 然后我们根据关系式的解读为:

数n对某方法的结果等于数n-1对该方法的结果与数n-2对该方法的结果

数2,数1对该方法的结果为1

3.递归、递推函数理解举例(非数学关系:&|!)

  • 快速排序为例进行说明
  • 代码见上,结构分析如下(仅考虑有效的数据操作)

  • 其中数据操作的意思为: 取数组中第一个元素,然后把小于它的元素放于左边,大于它的元素放于右边,如下:

  • 那么这个关系式可以写为:

F(array,left,right) = OP1 & F(array,left,i-1) & F(array,i+1,right)

终止条件:left>=right表示当左下标和右下标在同一个格子中,甚至更过分时,就会触发返回条件

因为终止条件,即递推到底条件有且只满足一次

  • 根据这个关系式,得到的结论为

输入一个数组,经过OP1(以该数组左下标对应的数值为参考,将比该数值小的置于左边,比该数值大的置于右边,参考数值下标为i),然后将i左边的数据在进行一次该操作,i右边的数据再进行一次操作,直到数据只剩一个甚至没有

4. 方法的执行顺序,上述分析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PH = 7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值