文章目录
跟随carl代码随想录刷题
语言:python
🙋递归法与迭代法哪个更好呢?
💁
- 递归法
空间复杂度
大一些,因为递归需要系统堆栈存参数返回值等等。- 递归
更容易让程序员理解
,但收敛不好,容易栈溢出。- 递归是
方便了程序员
,难为了机器
(各种保存参数,各种进栈出栈)。- ⭐️在实际项目开发的过程中我们是要
尽量避免递归
!因为项目代码参数、调用关系都比较复杂,不容易控制递归深度,甚至会栈溢出。——代码随想录
二叉树的递归遍历——深度优先
- 递归的实现就是:每一次递归调用都会把函数的
局部变量、参数值和返回地址
等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数。 - 三要素:
- 确定递归函数的参数和返回值
def traversal(root: TreeNode):
# 前序遍历
- 确定终止条件
if (root == None) return;
如果当前遍历的节点为空就返回
- 确定单层递归的逻辑
result.append(root.val)
# 中traversal(root.left)
# 左traversal(root.right)
# 右
- 确定递归函数的参数和返回值
- ⭐️
前序遍历
、中序遍历
、后序遍历
代码的唯一区别就是:中、左、右三个子句的顺序区别。
144. 二叉树的前序遍历
题目:给你二叉树的根节点 root ,返回它节点值的
前序
遍历。
完整代码如下
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 1. 初始化一个列表用于后续存放结果
result = []
# 2. 定义前序遍历的递归过程
def traversal(root:TreeNode):
if root == None:
return
result.append(root.val)
traversal(root.left)
traversal(root.right)
# 3. 调用前序遍历的递归函数
traversal(root)
# 4. 结果集
return result
94. 二叉树的中序遍历
题目:给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
完整代码如下
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 1. 初始化一个列表用于存放结果集
result = []
# 2. 中序遍历的函数
def traversal(root:TreeNode):
if root == None:
return
traversal(root.left)
result.append(root.val)
traversal(root.right)
# 3. 调用中序遍历函数
traversal(root)
# 4. 返回结果集
return result
145. 二叉树的后序遍历
题目:给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。
完整代码如下
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 1. 初始化一个列表用于存放结果集
result = []
# 2. 中序遍历的函数
def traversal(root:TreeNode):
if root == None:
return
traversal(root.left)
traversal(root.right)
result.append(root.val)
# 3. 调用中序遍历函数
traversal(root)
# 4. 返回结果集
return result
N叉树的递归遍历
589. N 叉树的前序遍历
题目:给定一个 n 叉树的根节点 root ,返回 其节点值的 前序遍历 。
n 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔(请参见示例)。
题目分析
和二叉树的写法几乎一样,唯一区别:
for ch in root.children: traversal(ch)
完整代码如下
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution:
def preorder(self, root: 'Node') -> List[int]:
# 1. 初始化一个列表用于后续存放结果
result = []
# 2. 定义前序遍历的递归过程
def traversal(root):
if root == None:
return
result.append(root.val)
for ch in root.children:
traversal(ch)
# traversal(root.right)
# 3. 调用前序遍历的递归函数
traversal(root)
# 4. 结果集
return result
590. N 叉树的后序遍历
题目:给定一个 n 叉树的根节点 root ,返回 其节点值的 后序遍历 。
n 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔(请参见示例)。
输入:root = [1,null,3,2,4,null,5,6]
输出:[5,6,3,2,4,1]
题目分析
- 和二叉树的写法几乎一样,唯一区别:
for ch in root.children: traversal(ch)
- 只用在上一题
N叉树的前序遍历
的基础上调换代码位置,即可实现后序遍历。
完整代码如下
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution:
def postorder(self, root: 'Node') -> List[int]:
# 1. 定义空列表用于存放结果集
result = []
# 2. 定义函数
def traversal(root):
if root == None:
return
for ch in root.children:
traversal(ch)
result.append(root.val)
# 调用函数
traversal(root)
# 返回函数
return result
递归刷题总结
递归算法
改为非递归算法
不是采用队列做辅助结构,而是栈
;- 一个递归算法必须包括
终止条件和递归部分
。 - 递归次数与每次划分后得到的分区处理顺序无关。
- 递归次数与各元素的初始排列有关。如果每一次划分后分区比较平衡,则递归次数少;如果划分后分区不平衡,则递归次数多。
- 无论是先长还是先短,
递归次数
是不变的,但递归深度
依据给定数据会在O(logN)~O(N)之间变化。原因是对两部分待排序区间进行递归形成的是二叉树,递归深度取决于二叉树的高度。
如果轴点恰好平分两个待排序部分,那么递归深度达到最优,即O(logN),但如果轴点使两部分极不平衡,那么二叉树就会退化为单链表,其深度会达到O(N)。 - 递归函数中的形参是
自动变量
。 - 对递归程序的优化的一般的手段为(
尾递归优化
)。【尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。 尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。 遗憾的是,大多数编程语言没有针对尾递归做优化】- 常见的优化手段有尾递归,迭代,循环
尾递归:在每一次递归的过程中保持了上一次计算的状态,也就是“线性迭代过程”
尾递归和一般的递归不同在对内存的占用,普通递归创建stack累积而后计算收缩,尾递归只会占用恒量的内存(和迭代一样)
//尾递归,进入下一个函数不再需要上一个函数的环境了,得出结果以后直接返回。 string story() { 四大名著之一 红楼梦:story() } //非尾递归,下一个函数结束以后此函数还有后续, //所以必须保存本身的环境以供处理返回值。 string story() { 四大名著之一 红楼梦:story(), 我最喜欢的书。 }
- 常见的优化手段有尾递归,迭代,循环