一.怎么写递归

  • 1.一定要搞清楚当前你的递归程序的功能是什么,以及需不需要返回值,如果需要,则返回的值是什么,如果对当前递归程序要完成的功能模棱两可、一知半解,那么逻辑肯定混乱,也就不容易写出正确的递归程序。
  • 2.找递归终止条件,根据函数的参数来看,看参数满足什么样的条件时是到了递归出口(常见的递归出口是,比如对于list(列表为空,只含有一个元素),对于二叉树(结点为空,结点是叶子结点),对于链表(头指针为空,只含有一个元素)等等),注意:这种递归终止情况是由:1.这个递归程序解决的就是一个终止条件,比如斐波那契就是为了算n=1或者2,归并排序一个列表,就是一个只含有一个元素的列表或者空列表。2这个终止情况是由上个状态递归到这里的,所以对于这个递归出口要注意,要联系上个状态看是直接返回某个值还是说对某个全局变量进行某种更改。
  • 3.就是做出递归关系式,当前状态的解决是怎么通过递归函数解决的,即怎么把一个大问题分割成若干小问题,通过解决若干个小问题再组合起来,来求解这个大问题(两种情况:1.先递归,再对递归回来的状态进行处理 2.先处理一下当前状态,然后再递归)。

1.以归并排序的代码作分析如下:

def merge_sort(alist):# 1-此函数的功能是对alist列表进行归并排序,之后返回排序好的新列表"""归并排序"""# 2-递归的出口是什么呢?对列表进行操作,不断分割列表,那么当列表长度小于等于1的时候,无可分割了,那就结束,直接返回这个列表即可n = len(alist)if n <= 1:return alistmid = n // 2# 3-要对当前列表进行归并排序,怎么通过递归解决当前这个问题呢# 递归关系式:merge_sort(alist) = (left_li=merge_sort(alist[:mid]) ,right_li = merge_sort(alist[mid:]),之后合并left_li和right_li)#3-1.对左半部分进行归并排序,直接调用此函数,这就是利用递归,直接处理左半部分(这里可能有点难以理解,但是不用想那么多merge_sort的功能不就是对alist进行归并排序嘛)left_li = merge_sort(alist[:mid])#3-2对右半部分同理right_li = merge_sort(alist[mid:])#3-3左半部分有序了,右半部分也有序了,那么再把左右部分合并到一起,使其整体有序了,就可以了#调用递归之后,还需要对递归得到的结果进行处理,才能最终得到结果#有的直接调用递归,并返回递归得到的值就可以解决这个问题了left_pointer, right_pointer = 0, 0result = []while left_pointer < len(left_li) and right_pointer < len(right_li):if left_li[left_pointer] <= right_li[right_pointer]:result.append(left_li[left_pointer])left_pointer += 1else:result.append(right_li[right_pointer])right_pointer += 1result += left_li[left_pointer:]result += right_li[right_pointer:]return result

2.以反转链表(leetcode-206)的递归代码作分析如下:

class Solution:def reverseList(self, head):# 1-此函数的功能是对以head为头结点的链表进行反转,之后返回反转后的新链表的头结点# 2-递归的出口是什么呢?对链表进行反转,显然head为空是一个出口,直接return,由于是对链表进行反转,所以当链表中只有一个元素的时候,即not head.next,也直接返回if not head or not head.next:return head# 3-要对当前以head为头结点的链表进行反转,怎么通过递归解决当前这个问题呢#递归关系式:reverseList(head) = (p=reverseList(head.next),再把p和head连接起来)# 3-1.先缩小范围,对head.next为头结点的链表进行反转,此时返回的头结点为pp = self.reverseList(head.next)# 经过上述递归之后,现在链表为:# head -> 1 <- 2 <- 3 <- 4 <- 5(p)# 3-2.经过上述递归后,head.next为头结点的链表已经进行了反转,此时,再将头结点加入即可# 注意:上述递归并没有对head结点进行操作,所以此时head的指向并没有改变!!!!这很重要head.next.next = headhead.next = Nonereturn p

3.以移除链表元素(leetcode-203)的递归代码作分析如下:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = Noneclass Solution(object):def removeElements(self, head, val):# 1-此函数的功能移除以head为头结点的链表中值为val的结点,并返回新链表的头结点""":type head: ListNode:type val: int:rtype: ListNode"""# 2-递归出口是什么,显然,链表为空时,直接返回headif not head:return head# 3-要对当前以head为头结点的链表中删除值为val的结点,怎么通过递归解决当前这个问题呢# 递归关系式:removeElements(head) = (nexttemp=removeElements(head.next),再把nexttemp和head连接起来)# 3-1.先缩小范围,对head.next为头结点的链表进行处理,此时返回的头结点为nexttempnexttemp = self.removeElements(head.next,val)# 3-2整个链表head右边的已经处理好了(即head右边的链表中值为val的结点已经删除了),此时就只需考虑head结点的值是否为val了if head.val == val:return nexttempelse:head.next = nexttempreturn head

二.递归的优化(以斐波那契数列为例)


# 写法1
def fibonacci_1(n):if n <= 1:return nreturn fibonacci_1(n - 1) + fibonacci_1(n - 2)# 写法2,利用自带的装饰器减少重复计算
import functools
# @functools.lru_cache(maxsize=128, typed=False)
# maxsize 是保存最近多少个调用的结果,最好设置为 2 的倍数,默认为 128。如果设置为 None 的话就相当于是 # maxsize 为正无穷了。还有一个参数是 type,如果 type 设置为 true,即把不同参数类型得到的结果分开保存,如 f(3) 和 f(3.0) 会被区分开#只有python3能用
@functools.lru_cache(None)
def fibonacci_1(n):if n <= 1:return nreturn fibonacci_1(n - 1) + fibonacci_1(n - 2)#写法3
# 带记忆化搜索的递归,避免了重复子问题的多次计算
def fibonacci_2(n, memo):if memo[n] != -1:return memo[n]elif n <= 1:memo[n] = nreturn memo[n]else:memo[n] = fibonacci_2(n - 1, memo) + fibonacci_2(n - 2, memo)return memo[n]# 自底向上的动态规划,时间复杂度O(n),空间复杂度O(n)
def fibonacci_3_1(n):res = [0, 1]for i in range(n - 1):res += [res[-1] + res[-2]]return res[-1]def fibonacci_3_2(n):res = [-1] * (n + 1)res[0], res[1] = 0, 1for i in range(2, n + 1):res[i] = res[i - 1] + res[i - 2]return res[-1]# 自底向上的动态规划,时间复杂度O(n),空间复杂度O(1)
# 因为当前状态只与之前的两个状态有关,所以我们不需要一个O(n)的
# 额外数组,只需要两个临时变量即可,空间复杂度变成了O(1)
def fibonacci_4(n):first, seccond = 0, 1for i in range(1, n):third = first + seccondfirst = seccondseccond = third# 上面的三行就等于下面的这一行# first, seccond = seccond, first + seccondreturn seccond
  • 举例子,[爬楼梯2]

#写法1,通过0%,很显然,直接递归,复杂度太大
n = int(input())
def fibonacci(n):if n < 3:return 1return fibonacci(n - 1) + fibonacci(n - 3)
print(fibonacci(n))#写法2,通过60%,没有设置最大栈深度(如果在PyCharm中执行n太大的话,比如500,就会提示:
# RecursionError: maximum recursion depth exceeded in comparison,而在牛客网上提交的话
# 会显示:请检查是否存在语法错误或者数组越界非法访问等情况,case通过率为60.00%
n = int(input())
import functools
@functools.lru_cache(None)
def fibonacci(n):if n < 3:return 1return fibonacci(n - 1) + fibonacci(n - 3)
print(fibonacci(n))#可以通过
n = int(input())
import sys
import functools
sys.setrecursionlimit(1000000)
@functools.lru_cache(None)
def fibonacci(n):if n < 3:return 1return fibonacci(n - 1) + fibonacci(n - 3)
print(fibonacci(n))
参考
  • 知乎-在学习数据结构与算法的时候,一旦出现递归就很难理解。请问对于递归有没有什么好的理解方法?
  • Python 缓存机制与 functools.lru_cache

对递归的理解以及怎么写递归程序相关推荐

  1. [转载] Python 递归 深入理解递归 Python递归剖析,绝对让你看懂!

    参考链接: Python | print()中的结束参数 目录 递归剖析 递归的两个过程 return 返回值 详解 递归思路二分法和递归尾递归递归练习题 递归剖析 递归真的很重要,之前学的时候,学的 ...

  2. 写二叉树程序时为什么总是报运行时错误_二叉树:一入递归深似海,从此offer是路人...

    给「代码随想录」一个星标吧! ❝ 一看就会,一写就废! ❞ 这次我们要好好谈一谈递归,为什么很多同学看递归算法都是"一看就会,一写就废". 主要是对递归不成体系,没有方法论,「每次 ...

  3. 昆虫繁殖(继续理解递推和递归)

    昆虫繁殖(继续理解递推和递归) /*[题目描述] 科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强.每对成虫过x个月产y对卵,每对卵要过两个月长成成虫.假设每个成虫不死,第一个月只有一对 ...

  4. 【思维风暴】算法迭代和递归的理解

    文章目录 递归与迭代 递归消耗内存的缺点 为什么要有迭代 需要用迭代消解递归的情况 不需要消解的递归 结束语 递归与迭代 递归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构.递归与迭代都涉及 ...

  5. python递归如何理解

    最近在做递归一些相关的东西,发现递归入门很容易,但要具体了解其实现过程,比较难以理解,在这里将自己这几天的摸索记录一下,写知乎的主要目的是为了给自己做笔记,在做笔记的同时,帮助后来人少走弯路.今天简要 ...

  6. Python 递归 深入理解递归 Python递归剖析,绝对让你看懂!

    目录 递归剖析 递归的两个过程 return 返回值 详解 递归思路 二分法和递归 尾递归 递归练习题 递归剖析 递归真的很重要,之前学的时候,学的一知半解,以为真正了解,每次想到递归,就记得一句:返 ...

  7. 蓝桥杯算法竞赛系列第二章——深入理解重难点之递归(上)

    铁汁们,递归(下)已经更新咯,欢迎铁汁们批评指正. 蓝桥杯算法竞赛系列第二章--深入理解重难点之递归(下)_安然无虞的博客-CSDN博客 目录 一.递归是什么? 二.如何理解"递归" ...

  8. 教你写递归——递归三部曲

    递归,很多时候看递归算法都是"一看就会,一写就废". 这主要是主要是对递归不成体系,没有方法论,每次写递归算法 ,都是靠玄学来写代码,代码能不能编过都靠运气. 这里帮助大家确定下来 ...

  9. 递归的理解(数据结构)

    在数据结构中,很多内容都应用到递归,递归在数据结构中至关重要 链接 递归定义:一个函数.概念或数学结构,如果在其定义或说明内部直接或间接地出现对其本身的引用,或者是为了描述问题的某一状态,必须要用至它 ...

最新文章

  1. 成功解FileNotFoundError: [Errno 2] No such file or directory: './data\\mnist\\train-images-idx3-ubyte'
  2. 干货丨深度迁移学习方法的基本思路(文末送书)
  3. 【Scala】Scala中的_ 和 _*分别代表什么
  4. L2-015. 互评成绩-PAT团体程序设计天梯赛GPLT
  5. clion opencv安装_Mac 下搭建 Clion + OpenCV4.x 的开发环境
  6. 情侣的网站代码java_GitHub - Mutiantian/lovers-website: 程序员的情侣网站 (programmer's website of lovers)...
  7. ArcMap基于PG数据库创建企业级地理数据库
  8. 观看影片《硅谷传奇》
  9. 二頌(金火互易過程)
  10. iOS内购(IAP)自动续订订阅类型服务端总结
  11. 解决Maven Not Authorized问题
  12. VS2017编写汇编并调用c库函数(msvcrt.lib)
  13. 微信小程序生成海报中二维码-----长按识别不了问题及处理方案
  14. 计算机软件研究方法与技术路线,研究方法与技术路线
  15. 位运算相关题目-一些小trick 1bit代表独立数字 求只出现一次的数字 无进位n进制数 n(-n) Boyer-Moore 投票算法 n(n-1)
  16. ​万字长文阐述前端技术浪潮与应用
  17. 如何使用java解析json文件并将其写入数据库
  18. 学习区块链要掌握哪些专项能力?区块链学习培训多长时间?
  19. python如果否则_Python传递参数(如果已定义),否则使用defau
  20. MyData 白皮书:一种以人为本的个人数据管理北欧模式 | ArcBlock 博客

热门文章

  1. Pytorch框架TorchScript模型转换方法
  2. Navicat Missing required library sqlite.dll,998
  3. 安卓开发学习之TCP通信
  4. 获取PancakeSwap Price
  5. python-pandapower电力系统潮流计算无法收敛情况解决方法
  6. 快手一键取关所有关注的人JS代码
  7. 常用汇编数据传输指令
  8. 习题4-8 高空坠球 (20分) 皮球从某给定高度自由落下,触地后反弹到原高度的一半,再落下,再反弹,……,如此反复。问皮球在第n次落地时,在空中一共经过多少距离?第n次反弹的高度是多少?
  9. PostgreSQL+PostGIS实现两坐标点之间最短路径查询算法函数(地图工具篇.12)
  10. 进程守护方案http://www.jianshu.com/p/89d06594dc5d