返回
从LeetCode刷剑指offer:轻松重建二叉树的七种方法
前端
2024-02-13 07:40:29
重建二叉树:7 种经典算法详解
一、重建二叉树的意义
二叉树是一种广泛应用于计算机科学中的数据结构,它由节点及其左右子树组成。重建二叉树是指根据其遍历序列(例如前序遍历或中序遍历)来构建一棵完整的二叉树。重建二叉树在算法、数据结构和计算机科学领域有着重要的应用,如构建哈夫曼树、二叉搜索树和二叉堆等。
二、重建二叉树的七种方法
1. 递归法(前序遍历 + 中序遍历)
思路:
- 前序遍历的第一个节点为根节点。
- 在中序遍历中找到根节点,将其左侧节点作为左子树,右侧节点作为右子树。
- 分别对左子树和右子树递归执行以上步骤,直到重建出整个二叉树。
代码示例:
def build_tree(preorder, inorder):
if not preorder or not inorder:
return None
root_val = preorder[0]
root_idx = inorder.index(root_val)
left_subtree = build_tree(preorder[1:root_idx+1], inorder[:root_idx])
right_subtree = build_tree(preorder[root_idx+1:], inorder[root_idx+1:])
return TreeNode(root_val, left_subtree, right_subtree)
2. 递归法(后序遍历 + 中序遍历)
思路:
- 后序遍历的最后一个节点为根节点。
- 在中序遍历中找到根节点,将其左侧节点作为左子树,右侧节点作为右子树。
- 分别对左子树和右子树递归执行以上步骤,直到重建出整个二叉树。
代码示例:
def build_tree(postorder, inorder):
if not postorder or not inorder:
return None
root_val = postorder[-1]
root_idx = inorder.index(root_val)
left_subtree = build_tree(postorder[:root_idx], inorder[:root_idx])
right_subtree = build_tree(postorder[root_idx:-1], inorder[root_idx+1:])
return TreeNode(root_val, left_subtree, right_subtree)
3. 非递归法(前序遍历 + 中序遍历)
思路:
- 使用两个栈,一个保存前序遍历序列,另一个保存中序遍历序列。
- 从前序栈中弹出第一个节点作为根节点。
- 在中序栈中找到根节点,将其左侧节点作为左子树,右侧节点作为右子树。
- 将左子树和右子树的根节点分别压入前序栈。
- 重复以上步骤,直到前序栈为空。
代码示例:
def build_tree(preorder, inorder):
if not preorder or not inorder:
return None
pre_stack = [preorder[0]]
in_stack = [inorder[0]]
while pre_stack:
root_val = pre_stack.pop()
root_idx = in_stack.index(root_val)
left_subtree = build_tree(preorder[1:root_idx+1], inorder[:root_idx])
right_subtree = build_tree(preorder[root_idx+1:], inorder[root_idx+1:])
in_stack.pop()
pre_stack.extend(right_subtree, left_subtree)
return TreeNode(root_val, left_subtree, right_subtree)
4. 非递归法(后序遍历 + 中序遍历)
思路:
- 使用两个栈,一个保存后序遍历序列,另一个保存中序遍历序列。
- 从后序栈中弹出最后一个节点作为根节点。
- 在中序栈中找到根节点,将其左侧节点作为左子树,右侧节点作为右子树。
- 将左子树和右子树的根节点分别压入后序栈。
- 重复以上步骤,直到后序栈为空。
代码示例:
def build_tree(postorder, inorder):
if not postorder or not inorder:
return None
post_stack = [postorder[-1]]
in_stack = [inorder[0]]
while post_stack:
root_val = post_stack.pop()
root_idx = in_stack.index(root_val)
left_subtree = build_tree(postorder[:root_idx], inorder[:root_idx])
right_subtree = build_tree(postorder[root_idx:-1], inorder[root_idx+1:])
in_stack.pop()
post_stack.extend(left_subtree, right_subtree)
return TreeNode(root_val, left_subtree, right_subtree)
5. 层序遍历法
思路:
- 将前序遍历和中序遍历序列同时存储在一个队列中。
- 从队列中取出第一个节点作为根节点。
- 在队列中找到根节点的左子树和右子树的根节点。
- 将左子树和右子树的根节点分别压入队列。
- 重复以上步骤,直到队列为空。
代码示例:
def build_tree(preorder, inorder):
if not preorder or not inorder:
return None
queue = [TreeNode(preorder[0])]
pre_idx = 1
in_idx = 0
while queue and pre_idx < len(preorder):
node = queue.pop(0)
if node.val == inorder[in_idx]:
in_idx += 1
if pre_idx < len(preorder):
node.left = TreeNode(preorder[pre_idx])
queue.append(node.left)
pre_idx += 1
if node.val == inorder[in_idx]:
in_idx += 1
if pre_idx < len(preorder):
node.right = TreeNode(preorder[pre_idx])
queue.append(node.right)
pre_idx += 1
return queue[0]
6. 哈夫曼树法
思路:
- 将前序遍历和中序遍历序列同时存储在一个优先队列中,权重为节点的频率。
- 从优先队列中取出权重最小的两个节点,作为左子树和右子树的根节点。
- 将左子树和右子树的根节点合并为一个新的节点,权重为左子树和右子树权重的和。
- 将新节点压入优先队列。
- 重复以上步骤,直到优先队列中只有一个节点。
代码示例:
import heapq
def build_tree(preorder, inorder):
if not preorder or not inorder:
return None
freq = {}
for val in preorder:
freq[val] = freq.get(val, 0) + 1
pq = []
for val, f in freq.items():
heapq.heappush(pq, (f, val))
while len(pq) > 1:
f1, val1 = heapq.heappop(pq)
f2, val2 = heapq.heappop(pq)
new_node = TreeNode((val1, val2), None, None)
heapq.heappush(pq, (f1+f2, new_node))
return pq[0][1]
7. 二叉搜索树法
思路:
- 将前序遍历序列存储在一个栈中。
- 从栈中弹出第一个节点作为根节点。
- 在栈中找到根节点的左子树和右子树的根节点。
- 将左子树和右子树的根节点分别压入栈。
- 重复以上步骤,直到栈为空。
代码示例:
def build_tree(preorder):
if not preorder:
return None
stack = [TreeNode(preorder[0])]
pre_idx = 1
while stack and pre_idx < len(preorder):
node = stack[-1]
if node.val > preorder[pre_idx]:
node.left = TreeNode(preorder[pre_idx])
stack.append(node.left)
pre_idx += 1
else:
node = stack.pop()
node.right = TreeNode(preorder[pre_idx])
stack.append(node.right)
pre_idx += 1
return stack[0]
三、常见问题解答
1. 如何选择重建二叉树的方法?
选择哪种重建