1 二叉树基础理论
1.1 二叉树种类
-
满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。深度为k的满二叉树有2^k-1个节点。
-
完全二叉树:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
- 二叉搜索树:二叉搜索树每个节点都有值,是一个有序树。满足:
- ① 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- ② 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- ③ 它的左、右子树也分别为二叉搜索树
- 平衡二叉搜索树,AVL(Adelson-Velsky and Landis)树: 平衡二叉搜索树首先是二叉搜索树,对于树中的任意一个节点,其左子树中的所有节点的值都小于该节点的值,而其右子树中的所有节点的值都大于该节点的值。并且满足平衡性质:对于树中的任意一个节点,其左子树和右子树的高度差(深度)的绝对值不超过1。这个条件确保了树在任何时候都保持大致平衡,从而保证了树的查找、插入和删除操作的时间复杂度都能保持在O(log n)。
1.2 二叉树的存储
分为链式存储和顺序存储。链式存储分别使用左右指针指向左右子树;顺序存储用数组存储完全二叉树,如果根节点在数组中索引为i,则左孩子和右孩子的数组下标分别为2i+1和2i+2。
1.3 二叉树遍历
-
深度优先遍历
- 前序遍历(递归法,迭代法):根左右
- 中序遍历(递归法,迭代法):左根右
- 后序遍历(递归法,迭代法):左右根
-
广度优先遍历
- 层次遍历(迭代法)
2 二叉树递归遍历
2.1 前序遍历
使用递归进行前中后序遍历需要注意三点:
1 函数的定义、输入和输出 2 终止条件,一般是树为空时候返回 3 单层递归的逻辑。
在前序遍历中,先将根节点的val存储到结果中,在递归将左子树、右子树的前序遍历加到结果中。注意递归也有两种写法,第一种写法和回溯类似,tranverse函数无返回值,对类变量进行操作;第二种和动态规划类似需要有返回值,通过函数的定义来分解子问题,将子问题的返回值推导出最终答案。
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
''' 递归法1 '''
self.res = []
self.traverse(root)
return self.res
def traverse(self, root:TreeNode):
if root is None:
return
self.res.append(root.val)
self.traverse(root.left)
self.traverse(root.right)
def preorderTraversal2(self, root: Optional[TreeNode]) -> List[int]:
''' 递归法2:利用函数定义来分解问题 '''
res = []
if not root:
return res
res.append(root.val)
res.extend(self.preorderTraversal2(root.left))
res.extend(self.preorderTraversal2(root.right))
return res
2.2 中序遍历
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
''' 递归法1 '''
self.res = []
self.traverse(root)
return self.res
def traverse(self, root:TreeNode):
if root is None:
return
self.traverse(root.left)
self.res.append(root.val)
self.traverse(root.right)
def inorderTraversal2(self, root: Optional[TreeNode]) -> List[int]:
''' 递归法2:利用函数定义来分解问题 '''
res = []
if not root:
return res
res.extend(self.inorderTraversal2(root.left))
res.append(root.val)
res.extend(self.inorderTraversal2(root.right))
return res
2.3 后序遍历
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
''' 递归法1 '''
self.res = []
self.traverse(root)
return self.res
def traverse(self, root:TreeNode):
if root is None:
return
self.traverse(root.left)
self.res.append(root.val)
self.traverse(root.right)
def postorderTraversal2(self, root: Optional[TreeNode]) -> List[int]:
''' 递归法2:利用函数定义来分解问题 '''
res = []
if not root:
return res
res.extend(self.postorderTraversal2(root.left))
res.extend(self.postorderTraversal2(root.right))
res.append(root.val)
return res
3 二叉树迭代遍历
3.1 前序遍历
使用递归的方法处理遍历时候,每一层递归调用会把函数的局部变量、参数值和返回地址都压入栈中,然后递归返回时候从栈顶弹出各项参数。因此可以使用栈结构来模拟递归的过程进行遍历:
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
if not root:
return res
stk = [root] # 栈记录变量的元素 根左右的压入顺序:先右再左,最先弹出左边元素
while stk:
top = stk.pop() # 弹出栈顶元素
res.append(top.val)
if top.right:
stk.append(top.right)
if top.left:
stk.append(top.left)
return res
3.2 中序遍历
中序比较特殊,根节点不像前序一样访问顺序和处理顺序一样。而是先访问到根节点,然后逐层往下到最左下的节点后才开始处理。在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。

class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
if not root:
return res
stk = [] # 注意先别加root!
cur = root # 迭代到左子树的最左叶节点
while cur or stk:
if cur is not None:
# 往左走
stk.append(cur)
cur = cur.left
else:
# 空了,回退到根节点,加入res后移动到right
cur = stk.pop()
res.append(cur.val)
cur = cur.right
return res
3.3 后序遍历
与前序遍历类似,需要得到左右根,先得到根右左,再反转结果。要得到根右左,需要在弹出一个节点时,在按照左孩子、右孩子的顺序加入到stk中。
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
if not root:
return res
stk = [root] # 栈记录变量的元素 根左右的压入顺序:先右再左,最先弹出左边元素
while stk:
top = stk.pop() # 弹出栈顶元素
# 根右左
res.append(top.val)
if top.left:
stk.append(top.left)
if top.right:
stk.append(top.right)
# 左右根
return res[::-1]
4 二叉树统一迭代遍历
TBC...
5 二叉树层序遍历
使用先进先出的队列存储树节点,每次迭代一层的节点,每个节点弹出时将非空的左右子节点依次加入队列。
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
res = []
if not root:
return res
queue = [root]
while queue:
size = len(queue)
level = [] # 存储当前元素
for i in range(size):
first = queue.pop(0)
level.append(first.val)
if first.left:
queue.append(first.left)
if first.right:
queue.append(first.right)
res.append(level) # 注意每一层需要单独放
return res