文章目录
1. 题目
给定一个二叉树,返回它的 中序 遍历。
1.1 示例
- 示例 1 1 1:
- 输入:
[1, null, 2, 3]
- 输出:
[1, 3, 2]
1.2 说明
- 来源: 力扣(LeetCode)
- 链接: https://leetcode-cn.com/problems/binary-tree-inorder-traversal/
1.3 进阶
递归算法很简单,你可以通过迭代算法完成吗?
2. 解法一(递归法)
2.1 分析
类似【LeetCode 二叉树专项】二叉树的前序遍历(144),二叉树的中序遍历使用递归的方式很容易实现,即按照访问 左子树 -> 根节点 -> 右子树
的方式遍历这棵树,而在访问左子树或者右子树的时候,按照同样的方式遍历,直到遍历完整棵树。可以看出,整个遍历过程天然具有递归的性质,因此可以直接用递归函数来模拟这一过程。
2.2 解答
from typing import List
class TreeNode:
def __init__(self, val=0, left: 'TreeNode' = None, right: 'TreeNode' = None):
self.val = val
self.left = left
self.right = right
class Solution:
def __init__(self):
self.tree = []
def _inorder(self, root: TreeNode):
if not root:
return
self._inorder(root.left)
self.tree.append(root.val)
self._inorder(root.right)
def postorder_traversal(self, root: TreeNode) -> List[int]:
self._inorder(root)
return self.tree
2.3 复杂度
- 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的节点数。每一个节点恰好被遍历一次。
- 空间复杂度: O ( n ) O(n) O(n) ,为递归过程中递归栈的开销和实例属性
self.tree
所占用空间两者组成,其中前者平均情况下为 O ( log n ) O(\log n) O(logn) ,最坏情况下树呈现链状,为 O ( n ) O(n) O(n) 。
3. 解法二(迭代法)
3.1 分析
虽然迭代形式的二叉树中序遍历仍然借用栈作为辅助的数据结构,但是要比前序遍历的迭代形式复杂一些,实现迭代形式的中序遍历主要步骤如下:
- 步骤一: 创建一个空的栈
stack
; - 步骤二: 初始化游标变量
cursor
使其引用root
; - 步骤三: 将
cursor
引用的节点压栈并且让cursor
指向cursor.left
,重复此操作直到cursor
引用None
; - 步骤四: 如果
stack
非空且cursor
引用None
,则重复下列步骤:- (a):从
stack
栈顶弹出节点并由node
引用; - (b):执行对弹出节点的遍历操作;
- (c):使得
cursor
引用node.right
; - (d):重复步骤三。
- (a):从
- 步骤五: 如果
cursor
引用None
且stack
为空,则中序遍历完成。
为便于理解,对于形如下列形式的二叉树,使用上述迭代步骤的具体情况如下:
1
/ \
2 3
/ \
4 5
- 步骤一: 创建一个空的栈
stack
:stack = []
;- 步骤二: 初始化游标变量
cursor
使其引用root
:cursor -> 1
;- 步骤三: 将
cursor
引用的节点压栈并且让cursor
指向cursor.left
,重复此操作直到cursor
引用None
:
cursor -> 1
;- 将
1
压栈:stack = [1]
;cursor -> 2
;- 将
2
压栈:stack = [1, 2]
;cursor -> 4
;- 将
4
压栈:stack = [1, 2, 4]
;cursor = None
。- 步骤四:
stack
非空且cursor
引用None
,重复下列步骤:
- (a):从
stack
栈顶弹出节点:stack = [1, 2]
;- (b):执行对于
4
对应节点的遍历操作;- (c):使得
cursor
引用4
对应节点的右子节点,即cursor = None
;- (d):由于
cursor = None
,步骤三不会做任何操作。
- (a):从
stack
栈顶弹出节点:stack = [1]
;- (b):执行对于
2
对应节点的遍历操作;- (c):使得
cursor
引用2
对应节点的右子节点,即cursor -> 5
;- (d):此时
cursor -> 5
,执行步骤三。- 步骤三: 将
cursor
引用的节点压栈并且让cursor
指向cursor.left
,重复此操作直到cursor
引用None
:
cursor -> 5
;- 将
5
压栈:stack = [1, 5]
;cursor = None
;- 步骤四:
stack
非空且cursor
引用None
,重复下列步骤:
- (a):从
stack
栈顶弹出节点:stack = [1]
;- (b):执行对于
5
对应节点的遍历操作;- (c):使得
cursor
引用5
对应节点的右子节点,即cursor = None
;- (d):由于
cursor = None
,步骤三不会做任何操作。
- (a):从
stack
栈顶弹出节点:stack = []
;- (b):执行对于
1
对应节点的遍历操作;- (c):使得
cursor
引用1
对应节点的右子节点,即cursor -> 3
;- (d):此时
cursor -> 3
,执行步骤三。- 步骤三: 将
cursor
引用的节点压栈并且让cursor
指向cursor.left
,重复此操作直到cursor
引用None
:
cursor -> 3
;- 将
3
压栈:stack = [3]
;cursor = None
;- 步骤四:
stack
非空且cursor
引用None
,重复下列步骤:
- (a):从
stack
栈顶弹出节点:stack = []
;- (b):执行对于
3
对应节点的遍历操作;- (c):使得
cursor
引用3
对应节点的右子节点,即cursor = None
;- (d):由于
cursor = None
,步骤三不会做任何操作。- 步骤五: 最终,
stack
为空且cursor
引用None
,中序遍历完成。
3.2 解答
根据上述分析,得到二叉树中序遍历的迭代形式如下:
from typing import List
class TreeNode:
def __init__(self, val=0, left: 'TreeNode' = None, right: 'TreeNode' = None):
self.val = val
self.left = left
self.right = right
class Solution:
def __init__(self):
self.tree = []
def iterative_inorder(self, root: TreeNode) -> List[int]:
if not root:
return self.tree
stack = []
cursor = root
while cursor:
stack.append(cursor)
cursor = cursor.left
while stack:
node = stack.pop()
self.tree.append(node.val)
cursor = node.right
while cursor:
stack.append(cursor)
cursor = cursor.left
return self.tree
实际上,上述代码在实现步骤三时有重复的代码实现,可进一步简化为如下代码:
from typing import List
class TreeNode:
def __init__(self, val=0, left: 'TreeNode' = None, right: 'TreeNode' = None):
self.val = val
self.left = left
self.right = right
class Solution:
def __init__(self):
self.tree = []
def succinct_iterative_inorder(self, root: TreeNode) -> List[int]:
if not root:
return self.tree
stack = []
cursor = root
while True:
if cursor:
stack.append(cursor)
cursor = cursor.left
elif stack:
node = stack.pop()
self.tree.append(node.val)
cursor = node.right
else:
break
return self.tree
def main():
""" Constructed binary tree is
1
/ \
2 3
/ \
4 5
"""
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
sln = Solution()
print(sln.succinct_iterative_inorder(root)) # [4, 2, 5, 1, 3]
if __name__ == '__main__':
main()
3.3 复杂度
- 时间复杂度: O ( n ) O(n) O(n),其中 n n n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。
- 空间复杂度: O ( n ) O(n) O(n)。空间复杂度取决于栈深度,而栈深度在二叉树为一条链的情况下会达到 O ( n ) O(n) O(n) 的级别。