一、递归遍历
1.递归算法三要素:
确定递归函数的参数和返回值: 在递归函数里加上递归的过程中需要处理的参数, 然后明确每次递归的返回值是什么,最后确定递归函数的返回类型。
确定终止条件: 递归算法运行的时候,经常会遇到栈溢出的错误,一般就是没写终止条件或者终止条件有误。
确定单层递归的逻辑: 确定每一层递归需要处理的信息,重复调用自己来实现递归的过程。
2.以前序遍历为例(遍历顺序:中左右)
1.确定递归函数的参数和返回值:参数根节点:root,返回int类型列表
def preorderTraversal(self, root: TreeNode) -> List[int]:
2.确定终止条件:当前遍历节点为空
if node is None:
return
3.确定单层递归的逻辑:前序遍历是中左右的顺序,所以在单层递归的逻辑,是要先取中节点的数值
res.append(node.val)
dfs(node.left)
dfs(node.right)
完整前序遍历:
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: TreeNode) -> List[int]:
res = []
def dfs(node):
if node is None:
return
res.append(node.val) #中
dfs(node.left) #左
dfs(node.right) #右
dfs(root)
return res
中序和后续只需将调整“中左右”的逻辑顺序为“左中右”和“左右中”
dfs(node.left)
res.append(node.val)
dfs(node.right)
dfs(node.left)
dfs(node.right)
res.append(node.val)
二、二叉树的迭代遍历
1.前序遍历迭代
每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以递归可以返回上一层位置。
由于栈的“先入后出”的性质,对于前序遍历,每次先处理中间节点,首先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。为什么要先加入 右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序。
stack = [root]
result = []
while stack:
node = stack.pop()
# 中节点先处理
result.append(node.val)
# 右孩子先入栈
if node.right:
stack.append(node.right)
# 左孩子后入栈
if node.left:
stack.append(node.left)
完整前序遍历——迭代法
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
# 根节点为空则返回空列表
if not root:
return []
stack = [root]
result = []
while stack:
node = stack.pop()
# 中节点先处理
result.append(node.val)
# 右孩子先入栈
if node.right:
stack.append(node.right)
# 左孩子后入栈
if node.left:
stack.append(node.left)
return result
2.中序遍历迭代:
前序遍历的顺序是中左右,先访问的元素是中间节点,要处理的元素也是中间节点,要访问的元素和要处理的元素顺序是一致的,都是中间节点,所以刚刚才能写出相对简洁的代码。
中序遍历是左中右,先访问的是二叉树顶部的节点,然后层层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中),所以处理顺序和访问顺序是不一致的。因而在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。
stack = [] # 不能提前将root节点加入stack中
result = []
cur = root
while cur or stack:
# 先迭代访问最底层的左子树节点
if cur:
stack.append(cur)
cur = cur.left
# 到达最左节点后处理栈顶节点
else:
cur = stack.pop()
result.append(cur.val)
# 取栈顶元素右节点
cur = cur.right
完整中序遍历——迭代法
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = [] # 不能提前将root节点加入stack中
result = []
cur = root
while cur or stack:
# 先迭代访问最底层的左子树节点
if cur:
stack.append(cur)
cur = cur.left
# 到达最左节点后处理栈顶节点
else:
cur = stack.pop()
result.append(cur.val)
# 取栈顶元素右节点
cur = cur.right
return result
3.后序遍历迭代
后序遍历是“左右中”,前序遍历是“中左右”——>"中右左"——>翻转result数组:“左右中”。因此可以修改前序遍历得到后序遍历
stack = [root]
result = []
while stack:
node = stack.pop()
# 中节点先处理
result.append(node.val)
# 左孩子先入栈
if node.left:
stack.append(node.left)
# 右孩子后入栈
if node.right:
stack.append(node.right)
# 将最终的数组翻转
return result[::-1]
#代码顺序里是中左右,因为栈是先进后出,可以发现与前序遍历的区别在于这四行的顺序
# if node.left:
# stack.append(node.left)
# 右孩子后入栈
# if node.right:
# stack.append(node.right)
完整的后序遍历迭代法:
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = [root]
result = []
while stack:
node = stack.pop()
# 中节点先处理
result.append(node.val)
# 左孩子先入栈
if node.left:
stack.append(node.left)
# 右孩子后入栈
if node.right:
stack.append(node.right)
# 将最终的数组翻转
return result[::-1]