运用递归处理问题

参考自《300分钟搞定数据结构与算法》 [中]苏勇 著

递归的基本性质:函数调用其本身

  • 递归是把大规模的问题不断变小,再进行推导的过程
  • 特点:可以使一个看似复杂的问题变得简洁和易于理解

运用递归算法的例子

一个递归算法的经典案例——汉诺塔问题

汉诺塔(又称:河内塔)是根据一个传说形成的数学问题:
有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:
1.每次只能移动一个圆盘;
2.大盘不能叠在小盘上面。
提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则。
问:如何移?最少要移动多少次?
——维基百科

这一经典问题的解法,其基本思想就是递归
假设有 A、B、C 三个塔,A 塔有N盘,目标是把这些盘全部移到 C 塔。那么先把 A 塔顶部的 N-1块盘移动到 B 塔,再把 A 塔剩下的大盘移到 C,最后把 B 塔的N-1块盘移到 C。
如此递归地使用下去, 就可以求解。

Python实现

# 将A塔的n个圆盘移动到C塔
def tower_of_hanoi(A, B, C, n):# 判断是否还有盘可移动if n > 0:# 先把 A 塔顶部的 N-1块盘移动到 B 塔tower_of_hanoi(A, ,C, B, n-1)# 再把 A 塔剩下的大盘移到 Cmove(A, C)# 最后把 B 塔的N-1块盘移到 Ctower_of_hanoi(B, A, C, n-1)

另一个递归算法的案例——展开Python的嵌套列表

将如下嵌套列表展开并保存为一个列表
[4, 5, [6, 7], [[8, 9], [10, 11], 12]]
展开结果为:[4, 5, 6, 7, 8, 9, 10, 11, 12]

递归思想解决思路
我们可以对给定列表中的元素逐个进行考虑,其结果有两种可能:

  1. 如果该元素不是列表,则直接加到结果列表中
  2. 如果该元素是一个列表,则还需对其进行展开方可添加到结果列表中

如此递归地对元素进行展开并添加至结果列表中, 就可以求解。

Python实现

def spread_list(spreadlist):# 结果列表res = []# 遍历列表中每一个元素for meb in spreadlist:# 判断元素类型if not isinstance(meb, list):# 如果该元素不是列表,则直接加到结果列表中res.append(meb)else:# 如果该元素是一个列表,则还需递归展开方可添加到结果列表中res.extend(spread_list(meb))# 返回结果return res

算法的基本思想

通过上面汉诺塔以及展开嵌套列表两个例子,我们总结递归算法的基本思想如下

  • 递归算法是一种自顶向下分析并解决问题的方法
  • 其通常把规模大的问题转化为规模小的相似的子问题,结合子问题的解来得出结果。
  • 在函数实现时,因为解决大问题的方法和解决小问题的方法往往是同一个方法,所以就产生了函数调用它自身的情况。

另外我们在解决问题时还需特别注意的一点是:解决问题的函数必须有明显的结束条件,这样就不会产生无限递归的情况了。

算法的一般格式

综上,我们总结出编写递归算法的一般格式如下

def recursion_arithmetic(参数):# 判断当前情况是否非法,若非法则立即返回if  illegality:return# 判断是否满足递归结束的条件,若满足则做相应求解等操作if satisfy condition:do something# 递归调用自身,缩小问题规模并返回子问题的解result1 = recursion_arithmetic(参数1)result2 = recursion_arithmetic(参数2)# 整合结果,对子问题的解以及当前数据进行分析,得出最终答案return combine(result1,result2)

Leetcode题目解析

接下来我们通过对Leetcode上面的题目分析来进一步认识利用递归算法求解问题

91.解码方法(难度:中等)
一条包含字母 A-Z 的消息通过以下方式进行了编码:

'A' -> 1
'B' -> 2
...
'Z' -> 26

给定一个只包含数字的非空字符串,请计算解码方法的总数。
示例 1

输入: "12"
输出: 2
解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。

示例 2

输入: "226"
输出: 3
解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。

递归思想解决思路
要运用递归来解决问题,首先我们应该思考对于某个问题,我们能否能将问题的规模变小呢,并且对我们要解决的问题产生帮助呢?
对于示例 2来说,给定编码为226,若不看最后一个字符6,对于22来说,若我们能求出对于22的解码有n种可能,那在这n种可能的基础上,加一个字符6在待解码的字符串22后面,只是在解码所得串后面加上字符F而已,解码方法依旧只有n种。
继续分析,对于6来说,若其前面一个字符如果是1或者2的话,那么它就可以构成16、26,所以我们还需再往前看一个字符,对于示例 2来说,6前面的字符是2,构成了26,若这个时候我们能求出26其前面的字符串解码方式有k种,那在这k种可能的基础上加上字符26,相当于在解码所得串后面加上字符Z而已,解码方法依旧只有k种。
所以总的解码个数就是n+k。对于示例 2的解码方式按此方法分析为2+1=3种。

Python实现

class Solution:def numDecodings(self, s: str) -> int:# 递归终止条件:若字符数少于1,则只有一种解码情况if len(s) <= 1:return 1# 统计解码方法count = 0# 取出当前元素及当前元素的前一个元素curr = s[-1]prev = s[-2]# 若当前元素不为0,解码方法与前面n-1个元素解码方法相同if not curr == '0':count = self.numDecodings(s[:-1])# 若当前元素与其前一个元素构成的数字在10~26以内,则解码方法还需加上前面n-2个元素的解码方法if prev == '1' or (prev == '2' and curr <= '6'):count += self.numDecodings(s[:-2])# 返回总的解码方法return count

该解法只为演示运递归思想解决问题,事实上针对该问题有时间复杂度为O(1)的最优解法

时间复杂度分析

  1. 迭代法
  2. 公式法

迭代分析法
迭代法是一种较为直观的分析递归算法时间复杂度的方法,下面通过对汉诺塔的例子时间复杂度的分析来进行说明。
问题的规模大小为n,假设递归函数的运行时间为T(n),对函数内部代码的执行情况进行分析,第一条执行语句为if,其对n的大小进行了一次判断,需占用1个单位的执行时间,接下来两次调用了递归函数,每一次调用问题的规模都比当前问题的规模减少了1,因此这里有2T(n-1)的执行时间,并假设nove操作需占用1个单位执行时间,因此我们可以得出以下表达式(if以及move执行规模为O(1)):

T(n) = 2 * T(n - 1) + O(1)

对上式进行分析,当没有盘子的时候,我们仅需进行一次if判断,所以T(0) = 1,对上式进行展开如下

T(n) = 2 * T(n - 1) + O(1)
T(n) = 2 (2 * T(n - 2) + 1) + 1 = 2^2 * T(n - 2) + (2 + 1)
T(n) = 2 (2 (2 * T(n - 3) + 1) + 1) + 1 = 2^3 * T(n - 3) + (4 + 2 + 1)

T(n) = 2^k *T(n - k) + (2^k - 1)

当n = k时,上式可化为T(n) = 2 * 2^n - 1 => O(n) = 2^n

公式法
计算递归函数复杂度最方便的工具
当递归函数的时间执行函数满足如下关系式时,可利用公式法求解:

T(n) = a * T(n/b) + f(n)
f(n)是指每次递归完毕后,额外的计算执行时间

当参数a,b都确定时,只看递归部分时间复杂度就是O(n^logb(a))

运用公式法求解递归函数的执行时间需分以下三种情况

  1. 当递归部分的执行时间O(n^logba) > f(n) 时,最终的时间复杂度就是O(n^logba)
  2. 当递归部分的执行时间O(n^logba) < f(n) 时,最终的时间复杂度就是f(n)
  3. 当递归部分的执行时间O(n^logba) = f(n) 时,最终的时间复杂度是O(n^logb(a))log(n)

对于情况1、2的解释为:时间复杂度取最耗时部分
一个利用公式法分析时间复杂度的例子
假设有时间执行函数:T(n) = 2 * T(n/4) + 1,分析如下:
a = 2, b = 4, f(n) = 1
代入公式,得到nlog4(2) = n^(1/2)
当n > 1时,n^(1/2) > 1,则时间复杂度就是:O(n^(1/2))

递归思想是一种重要的算法思想,很多算法都有其影子,如:二叉树遍历、归并排序、快速排序等,需重点掌握
欢迎关注我的博客:博客主页

数据结构与算法碎碎念之运用递归处理问题相关推荐

  1. 【Python数据结构与算法】(三):递归(Recursion)

    [Python数据结构与算法](三):递归(Recursion) ✨本文收录于<Python数据结构与算法>专栏,此专栏主要记录如何python学习数据结构与算法笔记.

  2. 记一次数据结构与算法作业:利用循环和递归输出1-N的正整数的程序分析比较

    随便记录一次数据结构与算法的分析作业,内容为分析循环和递归实现输出1-N的正整数的对比.从时间和空间上分析了两种方式实现的递归方法和循环区别. 一.数据记录图表 二.分析 第一张图表制作时由于在打游戏 ...

  3. 数据结构和算法详解(三)——递归、排序、散列表

    一.递归 一.什么是递归? 1.递归是一种非常高效.简洁的编码技巧,一种应用非常广泛的算法,比如DFS深度优先搜索.前中后序二叉树遍历等都是使用递归. 2.方法或函数调用自身的方式称为递归调用,调用称 ...

  4. 常考数据结构与算法:判断二叉树是否对称(迭代法,递归法)

    给定一棵二叉树,判断琪是否是自身的镜像(即:是否对称) 例如:下面这棵二叉树是对称的      1     /  \   2    2  / \    / \ 3 4  4  3 下面这棵二叉树不对称 ...

  5. Python数据结构与算法笔记(二):递归介绍及汉诺塔问题

    递归 内容介绍 func1和func2没有结束条件. 图解func递归过程: 长框代表func3,窄框代表print.函数执行过程是从上至下. 长框代表func4,窄框代表print.函数执行过程是从 ...

  6. 数据结构与算法(八)二分搜索树(Binary Search Tree)

    本文主要包括以下内容: 二分搜索树的基本概念 二分搜索树的基本操作 1. 插入 2. 删除 3. 查询 实现二分搜索树 二分搜索树的不足 二分搜索树的基本概念 二分搜索树(英语:Binary Sear ...

  7. 数据结构碎碎念(一)

    碎碎念 在大一学习C语言的时候,举过一个用栈实现的括号匹配算法,当时觉得很难,不过现在回顾起来,这个算法也算是比较简单的一个关于栈的应用了.而现在所常见的算法问题也都是什么中缀表达式转后缀表达式,双栈 ...

  8. 算法提高 分分钟的碎碎念

    1037: 算法提高 分分钟的碎碎念 时间限制: 1 Sec  内存限制: 256 MB 提交: 9  解决: 4 [ 提交][ 状态][ 讨论版] 题目描述 以前有个孩子,他分分钟都在碎碎念.不过, ...

  9. Java实现 蓝桥杯VIP 算法提高 分分钟的碎碎念

    算法提高 分分钟的碎碎念 时间限制:1.0s 内存限制:256.0MB 问题描述 以前有个孩子,他分分钟都在碎碎念.不过,他的念头之间是有因果关系的.他会在本子里记录每一个念头,并用箭头画出这个念头的 ...

最新文章

  1. 「屋漏偏逢连夜雨」,Log4j 漏洞还没忙完,新的又来了
  2. 紫书搜索 习题7-8 UVA - 12107 Digit Puzzle IDA*迭代加深搜索
  3. jquery导入数据_Web技术——简单的数据库编程
  4. Java Currency getInstance()方法与示例
  5. Git报错: OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443
  6. html设置设置字母间的距离,css如何设置字母间距?字母间距的设置方法
  7. 服务器上读取文件,服务器上读取文件
  8. Live2D Cubism Editor Pro v4.1.00 卡通动画模型制作工具中文版
  9. 音响在线测试软件,音响检测:漫步者煲箱软件
  10. ISO 9000 质量认证
  11. 0001-【linux系统】-用于生物信息分析该如何安装ubuntu系统?
  12. 如何在论文中正确引用参考文献(自动标注)
  13. 先吃奶油还是先吃蛋糕--推迟满足感
  14. RWEQ模型的土壤风蚀模数估算、其变化归因分析
  15. C# 文字识别(OCR)
  16. 用mybatis的generator自动生成代码--坑我都走了一遍,后面的同学别踩了
  17. 求1的阶乘~N的阶乘之和
  18. Codeforces Round #545 (Div. 2)
  19. 再探Handler(上)(Handler核心原理最全解析)
  20. SHA1加密技术文档说明

热门文章

  1. 如何在DW中运行PHP文件
  2. 请你讲讲分布式系统中的限流器一般如何实现?
  3. pdf合并的工具下载
  4. excel怎么按颜色统计单元格个数
  5. php图片素描化,ps怎么把图片变成素描图片
  6. 《俪影2046》v2.09 完美破解
  7. 在Oracle官网下载并安装JDK然后配置环境变量
  8. oracle 分组统计效率,Oracle 分组求和函数(rollup、cube、grouping sets)
  9. 张小龙-年薪近3亿的微信之父,他是如何做到的?
  10. leetcode第643题C++