目录
相关 Leetcode 问题
二叉树如何存储在内存中
二叉树可以使用两种常见的表示方式存储在内存中:基于指针(链式存储)结构 和 基于数组的结构。
基于指针的存储(链式存储)
在基于指针的存储中,每个节点都是一个对象,每个节点通常包含:
- 数据:节点中包含的值或信息。
- 左子节点指针:指向左子节点的指针。
- 右子节点指针:指向右子节点的指针。
class BinaryTree:
def __init__(self, value):
self.val = value # 节点中包含的值或信息
self.left = None # 指向左子节点的指针
self.right = None # 指向右子节点的指针
这种方法是动态的,允许树根据需要扩展。它特别适用于频繁插入和删除的操作,因为可以添加或移除节点,而无需重新组织整个结构。
基于数组的存储
在基于数组的二叉树存储中:
- 根节点:根元素存储在数组的起始位置。
- 左子节点:位于位置 n 的节点的左子节点位于位置 2n + 1。
- 右子节点:位于位置 n 的节点的右子节点位于位置 2n + 2。
这种方法在树是完全二叉树或完美二叉树时非常节省空间,因为它不需要任何指针。然而,对于稀疏的树来说,这种方法可能会因为数组中表示不存在节点的空索引而浪费存储空间。
对于二叉堆和其他需要在插入和删除过程中快速访问子节点和父节点的二叉树,这种方法特别高效。
class ArrayBasedBinaryTree:
def __init__(self):
self.tree = [] # 初始化一个空列表来存储树的元素
def root(self, value):
"""插入根节点"""
if not self.tree:
self.tree.append(value) # 如果树为空,插入根节点
def insert_left(self, parent_index, value):
"""插入左子节点"""
child_index = 2 * parent_index + 1
if child_index < len(self.tree):
self.tree[child_index] = value
else:
# 使用 None 扩展列表以保持结构
while len(self.tree) <= child_index:
self.tree.append(None)
self.tree[child_index] = value
def insert_right(self, parent_index, value):
"""插入右子节点"""
child_index = 2 * parent_index + 2
if child_index < len(self.tree):
self.tree[child_index] = value
else:
# 使用 None 扩展列表以保持结构
while len(self.tree) <= child_index:
self.tree.append(None)
self.tree[child_index] = value
def display(self):
"""显示树"""
return self.tree
不同类型的二叉树
不同类型的二叉树旨在满足特定需求,例如保持平衡或优化搜索操作。
二叉树类型 | 应用场景 |
---|---|
满二叉树 | 常用于需要简单高效的树结构,不涉及复杂操作的应用。 |
完全二叉树 | 理想的用于优先队列和基于堆的算法,在这些应用中,快速访问元素是必要的。 |
二叉搜索树 (BST) | 在频繁插入、删除和检索记录的数据库索引系统中表现良好。 |
AVL 树 | 在数据检索延迟较低的应用中,例如网络路由器中,平衡确保了最优的搜索时间。 |
满二叉树
满二叉树是一种二叉树,其中除了叶节点以外的每个节点都有两个子节点。换句话说,每个节点有零个或两个子节点。
- 每个节点有 0 个或 2 个子节点。
- 叶节点在同一层或相差一层。
完全二叉树
完全二叉树是一种二叉树,其中除最后一层外,所有层都是完全填充的,所有节点尽可能靠左排列。这个排列使完全二叉树特别适合存储在数组中,因为它们紧凑且靠左的结构减少了空间复杂度。
- 除最后一层外,所有层都是完全填充的。
- 节点尽可能靠左排列。
二叉搜索树 (BST)
在二叉搜索树中,对于每个节点,左子节点的值小于该节点的值,右子节点的值大于该节点的值。这种性质使得 BST 在搜索和检索操作中非常有用,因为每一步的左或右决策显著减少了搜索空间。
- 节点的左子树只包含键值小于该节点的节点。
- 节点的右子树只包含键值大于该节点的节点。
- 不允许有重复的节点。
Adelson-Velsky 和 Landis (AVL) 树
AVL 树是一种自平衡二叉搜索树,其中每个节点的两个子树的高度差不超过 1。如果在任何时候高度差超过 1,则进行重新平衡,以恢复这个特性。AVL 树对于查找密集型的应用进行了优化,在这些应用中,频繁的插入和删除需要快速重新平衡以维持高效的搜索时间。
平衡因子
平衡因子用于确定所需的旋转类型,以维持树的平衡。平衡因子必须始终为 -1
、0
或 1
。
平衡因子(节点)= 高度(右子树) - 高度(左子树)
重新平衡 AVL 树
当在 AVL 树中添加/删除节点时,需要执行旋转操作,以确保所有节点的平衡因子保持在允许的范围内。
树的遍历
深度优先搜索 (DFS)
DFS 沿着每条分支尽可能远地探索,然后回溯。可以通过系统的调用栈(递归)或显式栈数据结构实现。
- 用于解决具有唯一解的谜题,例如迷宫。
- 用于拓扑排序、调度问题、环检测等需要遍历路径的环境。
递归实现
def dfs(node):
if not node:
return
# 处理当前节点
print(node.val)
# 递归遍历左子树
dfs(node.left)
# 递归遍历右子树
dfs(node.right)
使用栈的 DFS
def dfs_stack(root):
stack = [root]
while stack:
node = stack.pop()
if node:
print(node.val)
stack.append(node.right)
stack.append(node.left)
前序遍历
前序遍历首先访问根节点,然后访问左子树,最后访问右子树。它较早捕获根节点,这对于需要复制或检查树结构的操作非常有用。
- 用于创建树的副本。
- 在编译器和表达式解析器等语法树应用中很有用,树结构与操作顺序一致。
前序遍历 - 递归实现
def preorder(node):
if not node:
return
print(node.val)
preorder(node.left)
preorder(node.right)
使用栈的实现
def preorder_stack(root):
stack = [root]
while stack:
node = stack.pop()
if node:
print(node.val)
stack.append(node.right)
stack.append(node.left)
中序遍历
中序遍历首先访问左子树,然后是根节点,最后是右子树。
- 常用于需要排序元素的二叉搜索树。
- 在需要按照层次级别处理节点的算法中很有用。
递归实现
def inorder(node):
if not node:
return
inorder(node.left)
print(node.val)
inorder(node.right)
使用栈的实现
def inorder_stack(root):
stack = []
current = root
while current or stack:
while current:
stack.append(current)
current = current.left
current = stack.pop()
print(current.val)
current = current.right
广度优先搜索 (BFS)
BFS 在移动到下一个深度级别之前,先探索当前深度级别的相邻节点。通过队列实现,优先处理最早访问的节点。
- 用于在无权图中找到从源节点到其他节点的最短路径。
- 适用于任何需要寻找最短路径的搜索算法,所有边的权重相等。
使用队列的实现
from collections import deque
def bfs(root):
if not root:
return
queue = deque([root])
while queue:
node = queue.popleft()
print(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
层次遍历
层次遍历逐层从上到下访问节点。这与树的广度优先搜索(BFS)相同。
- 广泛用于树和图的广度优先搜索操作,例如在无权图中寻找最短路径。
- 用于序列化树结构,以便可以进行重建。
使用队列实现
def level_order(root):
if not root:
return
queue = deque([root])
while queue:
node = queue.popleft()
print(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
如果您喜欢这篇文章,欢迎访问我的个人博客查看原文并参与讨论与互动【原文链接】