递归
详细讲解:https://www.zhihu.com/question/31412436/answer/683820765
递归的概念
- 1、递归:自己调用自己就是递归。(递归小套娃)
- 2、每进入一个函数调用,都会压栈,所以递归深度太深,则会出现栈溢出
- 3、同一个变量名在不同的函数栈,值是不同的
递归的三个特点
- base case:递归必须包含一个基础的出口,问题小到一定规模的时候,需要一个递归的“出口”返回
- recursive case:递归必须要包含一个可以解决的子问题,每一次递归我们都要分解它的参数,解决一个更小一点的问题
- toward the base case:递归必须要向着递归出口靠近
解决递归问题的顺序(重点*******)
- 1、首先定义递归函数功能:将函数框架先定义出来,函数名,函数参数
- 2、递归结束条件:需找出参数为啥时,递归结束,之后直接把结果返回(注意,这个时候我们必须能根据这个参数的值直接知道函数的结果是什么)
- 3、寻找函数的等价关系(递归表达式):
- 从上往下递归思想:例如求P(n),我们假设P(n-1)已经知道(可以看作是子问题的结果),则思考从P(n-1)的结果加上一些什么变量或者操作才能变成P(n)的结果
- 从下往上递推思想:例如求P(n),我们可以从n=1,2,3…依次类推去找到规律,找到类似于P(3)=P(1)+P(2)这样的规律,就可以推出P(n)=P(n-2)+P(n-1)
- 4、还有一种简单的思考方式:
- 1、递归出口
- 2、子问题
- 3、返回值
- 动态规划思想:最后需要考虑是否有重复计算的问题,将一些重复计算过的值使用中间变量/数组/哈希表保存起来,这样递归遇到之前算过的值就可以直接拿来用而不需要再去重复计算
掌握递归函数的编写
-
1、理解以下代码
-
# 这一段代码输出什么?为什么? def print_num_recursive(n): if n > 0: print_num_recursive(n-1) print(n) # 1 # 2 # 3 # ... # n-1 # n # 如果把print(n) 放在上面则输出什么? def print_num_recursive(n): if n > 0: print(n) print_num_recursive(n-1) # n # n-1 # ... # 3 # 2 # 1
-
-
2、正整数的阶乘
- n! = n*(n-1)*(n-2)*…*1
- f(n) = n*f(n-1)
-
3、汉诺塔问题
递归练习
斐波那契数
-
思路:
-
1、纯递归方式,时间复杂度O(2^n)
-
2、动态规划方式,时间复杂度O(n)
-
斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0, F(1) = 1 F(N) = F(N - 1) + F(N - 2), 其中 N > 1. 给定 N,计算 F(N)。
-
import functools class Solution: def fib(self, N: int) -> int: # 使用递归,时间复杂度O(2**n) # if N <= 1: # return N # return self.fib(N-1) + self.fib(N-2) # 迭代(动态规划),时间复杂度O(n) # a, b = 0, 1 # if N <= 1: # return N # for _ in range(N-1): # a, b = b, a+b # return b # 递归+动态规划,时间复杂度O(n) @functools.lru_cache(None) def fib(self, N: int) -> int: if N <= 1: return N return self.fib(N-1) + self.fib(N-2)
-
反转链表
-
class LinkedListNode(): def __init__(self, value, next=None): self.value = value self.next = next def gen_linked_list(nums): if not nums: return None head = LinkedListNode(nums[0]) prev = head for i in range(1, len(nums)): node = LinkedListNode(nums[i]) prev.next = node prev = node return head def print_linked_list(head): cur = head while cur: print('{}->'.format(cur.value), end='') cur = cur.next print('nil') class Solution(object): def reverseList(self, head): """ :type head: ListNode :rtype: ListNode """ # 迭代 时间复杂度:O(n),空间复杂度:O(1) # pre = None # cur = head # while cur: # nextnode = cur.next # cur.next = pre # pre = cur # cur = nextnode # return pre # 递归,时间复杂度:O(n),空间复杂度:O(1) if not (head and head.next): return head newhead = self.reverseList(head.next) nextnode = head.next nextnode.next = head head.next = None return newhead # pytest -s 'filepath' def test_reverselist(): L = [1,2,3,4,5] head = gen_linked_list(L) print() print_linked_list(head) pre = Solution().reverseList(head) print_linked_list(pre)
全排列
-
思路:
-
1、寻找子问题:当数列有三个元素,可以将第一个数固定,则后面的数继续递归操作,同理数列变成两个元素的话,也是固定第一个数,直到数列只有一个数的时候返回数列
-
给定一个 没有重复 数字的序列,返回其所有可能的全排列。 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
-
class Solution: def permute(self, nums): res = [] def per(nums, beg, end): if beg == end-1: res.append(nums[:]) return for i in range(beg, end): nums[i], nums[beg] = nums[beg], nums[i] per(nums, beg+1, end) nums[i], nums[beg] = nums[beg], nums[i] per(nums, 0, len(nums)) return res
-