复习递归函数三要素:递归函数的参数和返回值、终止条件以及单层递归的逻辑。
前序遍历:144. 二叉树的前序遍历 - 力扣(Leetcode)
# 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]:
result = []
# 递归法
def dfs(node):
if not node:
return # 节点为空,返回
result.append(node.val)
dfs(node.left)
dfs(node.right)
# return result # result用全局变量,不用返回值
dfs(root)
return result
中序遍历:94. 二叉树的中序遍历 - 力扣(Leetcode)
# 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]:
result = []
# 递归法
def dfs(node):
if not node:
return # 节点为空,返回
dfs(node.left)
result.append(node.val)
dfs(node.right)
# return result # result用全局变量,不用返回值
dfs(root)
return result
后序遍历:145. 二叉树的后序遍历 - 力扣(Leetcode)
# 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]:
result = []
# 递归法
def dfs(node):
if not node:
return # 节点为空,返回
dfs(node.left)
dfs(node.right)
result.append(node.val)
# return result # result用全局变量,不用返回值
dfs(root)
return result
三种遍历的写法是统一的,掌握其中一种递归逻辑,另外两种遍历方式只需要更改单层递归的遍历顺序即可,时间复杂度都是O(n)。
迭代法中使用额外的栈空间模拟递归流程,其中前序和后序遍历的代码逻辑基本相同:
# 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]:
# 迭代法
stack = []
result = []
if root is None:
return result
stack.append(root)
while stack:
cur = stack.pop()
result.append(cur.val)
if cur.right is not None:
stack.append(cur.right)
if cur.left is not None:
stack.append(cur.left)
return result
# 后序遍历
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 迭代法
stack = []
result = []
if root is None:
return result
stack.append(root)
while stack:
cur = stack.pop()
result.append(cur.val)
if cur.left is not None:
stack.append(cur.left)
if cur.right is not None:
stack.append(cur.right)
result.reverse()
return result
由于栈先入后出,要想按前序的中左右顺序将节点值加入result数组,单层递归逻辑中需要先将右节点入栈,同时注意空节点不入栈;后序遍历并不意味着根节点最后入栈,而是巧妙地通过前序遍历后将最后的结果数组反转实现,反转前result中的遍历顺序是中右左,反转过来即为后序的左右中。
而中序遍历会稍微不同。由于遍历节点和处理节点(收集结果)的顺序不一致,需要将两种逻辑分开,增加一个指针用于遍历,当遇到节点的左子节点为空就可以开始从栈中收集结果(因为左子节点为空时,前一个入栈的节点就对应当前节点,左为空,结果可以跳过,下一步就是收集当前节点的值,然后遍历右节点):
# 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]:
# 迭代法(代码随想录)
stack, result = [], []
if not root:
return 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
用具体例子模拟一下就能想明白这个过程,这个迭代思路确实很奇妙!
用中序遍历的逻辑将迭代法统一起来,增加一个标记:要处理的节点放入栈之后,紧接着放入一个空指针作为标记。下面贴的是代码随想录中的中序遍历代码,其他两种遍历顺序只需要调整非空节点中的添加节点顺序。跟随代码逻辑可以理解这种做法,但自己其实是不容易想到的,死记代码容易有错漏,还得多看多理解,将思路融汇于心才行。
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st = []
if root:
st.append(root)
while st:
node = st.pop()
if node != None:
if node.right: #添加右节点(空节点不入栈)
st.append(node.right)
st.append(node) #添加中节点
st.append(None) #中节点访问过,但是还没有处理,加入空节点做为标记。
if node.left: #添加左节点(空节点不入栈)
st.append(node.left)
else: #只有遇到空节点的时候,才将下一个节点放进结果集
node = st.pop() #重新取出栈中元素
result.append(node.val) #加入到结果集
return result
二叉树遍历比较基础,递归法很好掌握,迭代法需要多熟悉。