漫话算法[递归]:从盗梦空间到掌握递归思想!

快来和叮当学算法吧!

From All CV to No CV!我的梦想是编程不再CV,算法不再死记硬背!

我将算法思想及套路做成了好玩的有趣的视频欢迎你观看偶!

bilibili视频讲解递归

漫话算法[递归&迭代]从盗梦空间到掌握递归思想!

Leetcode五杀系列视频讲解

致力于通过至少5个算法问题!让你发现算法的套路!很快你就能和我一样入门算法并解决算法问题!

Leetcode刷题五杀系列[递归篇]


并且我将常用的算法模板已经上传到了我的github!

在这里插入图片描述

知识点路线

通过本文的训练你能解除的疑问

  • 什么是递归?什么是尾递归?
  • 递归与迭代如何转换?
  • 递归和迭代的区别及对比?
  • 如何对递归算法优化?
  • 什么是自顶向下的设计?什么是自顶向上的设计?
  • 什么是递推关系?
  • 自顶向下(从未知到已经)通常会出现重复计算如何优化?

盗梦空间

递归就好比盗梦空间而每一层“梦境”(递归)中逻辑都是相同的

  • [下潜]:进入下一层递归!
  • [回溯]:回到上一层,最终回到第一层就得到了答案!

在这里插入图片描述

递归

在这里插入图片描述

分类

  • 直接递归(自己调自己):func1->func1
  • 间接递归(间接调自己):func1->func2->func1
  • 尾递归:出现在程序的最后一行且值出现一次

组成

  • 递归出口:终止条件,解决问题一般要从递归的终止时开始思考!
  • 递归体:构造[递归不变式]!

迭代

在这里插入图片描述

简介

  • 基本算法编程技巧,解决问题的难点在于如何构造[循环不变式]

组成

  • 循环出口:终止条件,解决时候要考虑请求while循环条件如[二分查找]的<=和<的情况的含义!
  • 循环体:构造[循环不变式]!

算法框架

递归

public void recursion() { 
  // 1.[终止条件] 
  if (最后一层) { 
    // [处理最后逻辑] 
    return; 
  }
  // 2.[递归体]: 递推关系(不变式)
  f(n) = f(.)....
      
  // [下潜] 
  recursion(进入下一层"梦境"); 
  // 无逻辑即为[尾递归],还有逻辑[非尾递归]  
}

迭代

public void iteration() { 
  // 1.[终止条件] 
  while (最后一层) {
    // 2.[循环体]: 递推关系(不变式)
    f(n) = f(.)....
    // [变量重置]同递归中的[下潜]    
  }
}

在这里插入图片描述

尾递归

尾递归常常可以与迭代相互转换,但java8并未对尾递归进行优化,因此还是无法避免函数栈溢出的情况!

private static int recursion(int n, int f_2, int f_1) {
    // 1.[递归出口]
    if (n <= 2) {
        return f_1;// f_1是最后的res
    }

    // 2.[尾递归]
    return recursion(n-1, f_1, f_2+f_1);
}

递归的表现形式

在这里插入图片描述

直接递归

自己调自己

public int function() {
    return function();
}

在这里插入图片描述

间接递归

间接调自己

public int function1() {
    return function2();
}

public int function2() {
    return function1();
}

在这里插入图片描述

递推关系

递推属于[迭代算法]中的技巧但个人认为无论是递归还是迭代都可以利用递推的思想解决问题

构造[不变式]求解问题实际上个人的理解就是利用数学归纳法的思想!

  • 步骤1:基底(base)即证明简单情况成立!
  • 步骤2:归纳(induction)即证明f(k)成立能推出f(k+1)成立!

f ( n ) = n × ( n + 1 ) 2 f(n)=\frac{n \times(n+1)}{2} f(n)=2n×(n+1)

意义:显然使用优秀的的递推公式、数学模型,会得到更高效的算法!

// 对比[普通写法]和用[递推方程]的写法
/**
  * 叮当求和
  * @param n
  */
public static void dingdangSum(int n) {
    int sum = 0;
    for (int i = 1; i <= n;i++) {
        sum += i;
    }
}

/**
  * 高斯求和
  * @param n
  */
public static void gaussSum(int n) {
    int sum = n*(n+1)/2;// O(1)
}

举例:高斯求和
f ( n ) = n × ( n + 1 ) 2 f(n)=\frac{n \times(n+1)}{2} f(n)=2n×(n+1)

  • 步骤1:证明基底:f(0)=0,将0带入公式

  • 步骤2:归纳

    • 假设f(k)成立
    • 证明f(k+1)成立
    1. k+1带入公式中
      0 + 1 + 2 + . . . . + k + ( k + 1 ) = ( k + 1 ) × ( ( k + 1 ) + 1 ) 2 0+1+2+....+k+(k+1) = \frac{(k+1) \times((k+1)+1)}{2} 0+1+2+....+k+(k+1)=2(k+1)×((k+1)+1)
  1. 将左边分为0~k和(k+1),即f(k)+(k+1)
    k × ( k + 1 ) 2 + ( k + 1 ) = ( k + 1 ) × ( ( k + 1 ) + 1 ) 2 \frac{k \times(k+1)}{2}+(k+1) = \frac{(k+1) \times((k+1)+1)}{2} 2k×(k+1)+(k+1)=2(k+1)×((k+1)+1)

  2. 继续合并左侧

k × ( k + 1 ) 2 + 2 ( k + 1 ) 2 = ( k + 1 ) × ( ( k + 1 ) + 1 ) 2 \frac{k \times(k+1)}{2}+\frac{2(k+1)}2 = \frac{(k+1) \times((k+1)+1)}{2} 2k×(k+1)+22(k+1)=2(k+1)×((k+1)+1)

  1. 左侧继续合并
    ( k + 1 ) ( k + 2 ) 2 = ( k + 1 ) × ( ( k + 1 ) + 1 ) 2 \frac{(k+1)(k+2)}2 = \frac{(k+1) \times((k+1)+1)}{2} 2(k+1)(k+2)=2(k+1)×((k+1)+1)

  2. 右侧合并发现左右相等,这个就是[递推公式]
    ( k + 1 ) ( k + 2 ) 2 = ( k + 1 ) × ( k + 2 ) 2 \frac{(k+1)(k+2)}2 = \frac{(k+1) \times(k+2)}{2} 2(k+1)(k+2)=2(k+1)×(k+2)

在这里插入图片描述

自顶向下

自顶向下即是从未知到已经,从大问题到简单情况!

在这里插入图片描述

自底向上

从简单情况base case 到想要求解的大问题!
在这里插入图片描述

递归的复杂度计算

时间复杂度:即是递归经过的节点数

空间复杂度:递归有额外的函数栈开销因此

这里以斐波那契的递归树来讲解,递归二叉树的节点数就是递归经过的节点是时间复杂度递归树的高度及是空间复杂度

在这里插入图片描述

优化的方式

使用迭代

在实际工作中尽量使用迭代,避免使用“递归”时因为少考虑了递归终止条件引发“死递归”导致的cpu性能消耗等问题。

使用记忆化搜索

将递归中已经计算过的问题存储起来,下次计算的时候再取相应的值就可以了!斐波那契数列这道题为例可以将时间复杂度优化至线性阶O(n)

实战训练吧!

理论再多都不如实战,带着这些思想是去解决问题吧!

  • [尾递归]总是出现再递归尾部且只有一次
  • [尾递归]可以与迭代无缝转换
  • [非尾递归]通常可以使用栈来代替[函数栈]转换或者使用[队列]完成BFS的迭代
  • 递归通常可以使用[记忆化搜索]进行优化,或者使用迭代方式降低函数栈的消耗
  • 递归和迭代之间转换的突破点就是找到**[不变式]**
  • 二叉树、链表等通常可以使用递归完成!这是因为他们的天然的递归结构决定的,每一层有相似的特性!
  • 递归的复杂度计算
    空间复杂度:需要额外的空间消耗
    时间复杂度:其实就是去计算递归树的节点数目!或则递归经过了多少个链表节点!

Easy-Programming(我的开源项目: 漫话编程)

在这里插入图片描述
在这里插入图片描述

代码获取

我会持续更新Leetcode刷题的题解以各种各样你想要的!我会同步到线上博客已经微信公众号:CVBear欢迎你的订阅!
在这里插入图片描述

代码获取 Github传送门 帮我点个start鼓励我一下呗我!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值