第5章 树和二叉树
5.1 树和二叉树的定义
5.1.1 树的定义
树(Tree) 是一种常见的数据结构,用于描述具有层次结构的关系。树由一个根节点和若干子树组成,每个子树又可以递归地看作是一棵树。
树的特点:
- 树是一个有限的节点集合,包含一个根节点(唯一入口)。
- 除根节点外,每个节点都有且仅有一个父节点。
- 每个节点可以有零个或多个子节点,子节点之间互不相交。
树的表示:
- 图示:
mathematica
A
/ | \
B C D
/ \
E F
5.1.2 树的基本术语
- 节点(Node): 树中的基本元素。
- 根节点(Root): 没有父节点的节点(如上图的 A)。
- 父节点(Parent): 一个节点的直接上级节点。
- 子节点(Child): 一个节点的直接下级节点。
- 叶节点(Leaf): 没有子节点的节点(如 E、F、C、D)。
- 节点的度(Degree): 一个节点的子节点数量。
- 树的度(Degree of Tree): 树中所有节点度的最大值。
- 层次(Level): 从根节点开始计算节点的深度,第 1 层是根节点,第 2 层是根的子节点,以此类推。
- 高度(Height): 树中节点的最大层次。
- 路径(Path): 从一个节点到另一个节点的路线。
5.1.3 二叉树的定义
二叉树(Binary Tree) 是一种特殊的树结构,每个节点最多有两个子节点,分别为左子节点 和右子节点。
二叉树的特点:
- 每个节点最多有两个子节点,分别称为左子节点和右子节点。
- 二叉树是一种递归结构,每个子树也都是一棵二叉树。
5.2 案例引入
案例:家庭关系模型
假设有如下家庭关系:
- 父亲 A 有两个孩子:B 和 C。
- B 有两个孩子:D 和 E。
- C 没有孩子。
用二叉树表示如下:
mathematica
A
/ \
B C
/ \
D E
此二叉树可以存储为一个结构化数据,便于层级查询和操作。
5.3 树和二叉树的抽象数据类型定义
抽象数据类型(ADT)定义
树的抽象数据类型:
plaintext
ADT 树 {
数据对象:树是一个节点的集合,每个节点存储数据及其层次关系。
操作:
Insert(v): 插入节点 v;
Delete(v): 删除节点 v;
Find(v): 查找节点 v;
Traverse(): 遍历树的所有节点;
GetHeight(): 获取树的高度。
}
二叉树的抽象数据类型:
plaintext
ADT 二叉树 {
数据对象:每个节点最多有两个子节点(左子节点和右子节点)。
操作:
PreOrder(): 前序遍历;
InOrder(): 中序遍历;
PostOrder(): 后序遍历;
Insert(v): 插入节点 v;
Delete(v): 删除节点 v。
}
5.4 二叉树的性质和存储结构
5.4.1 二叉树的性质
- 性质1: 二叉树的第 i 层最多有 2^(i-1) 个节点。
- 性质2: 深度为 k 的二叉树最多有 2^k - 1 个节点。
- 性质3: 对于任何二叉树,叶子节点数 n0 和度为2的节点数 n2 满足关系:n0 = n2 + 1。
- 性质4: 完全二叉树的节点可以用数组顺序存储,且节点索引具有以下性质:
- 索引为 i 的节点,其左子节点索引为 2i,右子节点索引为 2i+1,父节点索引为 floor(i/2)。
5.4.2 二叉树的存储结构
- 顺序存储:
- 使用数组存储二叉树的节点。
- 索引规则:
- 2i 表示左子节点;
- 2i+1 表示右子节点;
- floor(i/2) 表示父节点。
- 示例:完全二叉树:
css
A
/ \
B C
/
D
存储为:[A, B, C, D, null, null, null]
- 链式存储:
-
每个节点用结构体或类存储,包括数据域和左右子节点指针。
pythonclass TreeNode: def __init__(self, value): self.data = value self.left = None self.right = None
5.5 遍历二叉树和线索二叉树
5.5.1 遍历二叉树
-
前序遍历(PreOrder): 根 -> 左 -> 右。
pythondef preorder(node): if node: print(node.data) preorder(node.left) preorder(node.right)
-
中序遍历(InOrder): 左 -> 根 -> 右。
pythondef inorder(node): if node: inorder(node.left) print(node.data) inorder(node.right)
-
后序遍历(PostOrder): 左 -> 右 -> 根。
pythondef postorder(node): if node: postorder(node.left) postorder(node.right) print(node.data)
5.5.2 线索二叉树
线索二叉树 使用空指针存储前驱和后继信息,便于遍历。
5.6 树和森林
-
树的存储结构:
- 双亲表示法
- 孩子表示法
- 孩子兄弟表示法
-
森林与二叉树转换:
- 将森林中的每棵树转换为二叉树,并通过右指针连接兄弟节点。
5.7 哈夫曼树及其应用
5.7.1 哈夫曼树的基本概念
哈夫曼树(Huffman Tree) 是一种最优二叉树,用于构造最短编码路径。
5.7.2 哈夫曼树的构造算法
python
def huffman_tree(weights):
nodes = [TreeNode(w) for w in weights]
while len(nodes) > 1:
nodes = sorted(nodes, key=lambda x: x.data)
left = nodes.pop(0)
right = nodes.pop(0)
parent = TreeNode(left.data + right.data)
parent.left = left
parent.right = right
nodes.append(parent)
return nodes[0]
5.7.3 哈夫曼编码
哈夫曼编码 是一种基于哈夫曼树的压缩编码算法。
5.8 案例分析与实现
在这一部分,我将结合具体的代码实现,分析树和二叉树的常见应用场景,包括以下案例:
- 案例1:用链式存储构造二叉树
- 案例2:实现并演示二叉树的前序、中序、后序遍历
- 案例3:构造哈夫曼树并生成哈夫曼编码
- 案例4:树和森林之间的转换
案例1:用链式存储构造二叉树
问题描述
我们希望构造一个二叉树,二叉树的结构如下:
mathematica
A
/ \
B C
/ \
D E
实现代码
python
class TreeNode:
def __init__(self, value):
self.data = value
self.left = None
self.right = None
# 构造二叉树
def create_tree():
root = TreeNode('A')
root.left = TreeNode('B')
root.right = TreeNode('C')
root.left.left = TreeNode('D')
root.left.right = TreeNode('E')
return root
# 测试构造函数
root = create_tree()
print(f"Root: {root.data}, Left: {root.left.data}, Right: {root.right.data}")
输出
mathematica
Root: A, Left: B, Right: C
图示
构造的二叉树结构:
mathematica
A
/ \
B C
/ \
D E
案例2:实现并演示二叉树的前序、中序、后序遍历
问题描述
使用递归实现二叉树的三种遍历方式:
- 前序遍历(根 -> 左 -> 右)
- 中序遍历(左 -> 根 -> 右)
- 后序遍历(左 -> 右 -> 根)
实现代码
python
# 前序遍历
def preorder(node):
if node:
print(node.data, end=" ")
preorder(node.left)
preorder(node.right)
# 中序遍历
def inorder(node):
if node:
inorder(node.left)
print(node.data, end=" ")
inorder(node.right)
# 后序遍历
def postorder(node):
if node:
postorder(node.left)
postorder(node.right)
print(node.data, end=" ")
# 测试遍历函数
root = create_tree()
print("Preorder Traversal: ", end="")
preorder(root)
print("\nInorder Traversal: ", end="")
inorder(root)
print("\nPostorder Traversal: ", end="")
postorder(root)
输出
mathematica
Preorder Traversal: A B D E C
Inorder Traversal: D B E A C
Postorder Traversal: D E B C A
图示
前序遍历过程:
rust
访问顺序:A -> B -> D -> E -> C
中序遍历过程:
rust
访问顺序:D -> B -> E -> A -> C
后序遍历过程:
rust
访问顺序:D -> E -> B -> C -> A
案例3:构造哈夫曼树并生成哈夫曼编码
问题描述
给定一组权值 {5, 9, 12, 13, 16, 45}
,构造哈夫曼树,并生成哈夫曼编码。
实现代码
python
import heapq
class HuffmanNode:
def __init__(self, weight, char=None):
self.weight = weight
self.char = char
self.left = None
self.right = None
# 定义节点的比较规则
def __lt__(self, other):
return self.weight < other.weight
# 构造哈夫曼树
def build_huffman_tree(weights, chars):
heap = [HuffmanNode(weights[i], chars[i]) for i in range(len(weights))]
heapq.heapify(heap)
while len(heap) > 1:
left = heapq.heappop(heap)
right = heapq.heappop(heap)
parent = HuffmanNode(left.weight + right.weight)
parent.left = left
parent.right = right
heapq.heappush(heap, parent)
return heap[0]
# 生成哈夫曼编码
def generate_huffman_codes(node, code="", huffman_codes={}):
if node:
if node.char:
huffman_codes[node.char] = code
generate_huffman_codes(node.left, code + "0", huffman_codes)
generate_huffman_codes(node.right, code + "1", huffman_codes)
return huffman_codes
# 测试
weights = [5, 9, 12, 13, 16, 45]
chars = ['a', 'b', 'c', 'd', 'e', 'f']
huffman_tree = build_huffman_tree(weights, chars)
huffman_codes = generate_huffman_codes(huffman_tree)
print("Huffman Codes:", huffman_codes)
输出
css
Huffman Codes: {'f': '0', 'c': '100', 'd': '101', 'a': '1100', 'b': '1101', 'e': '111'}
图示
哈夫曼树结构:
scss
(*)
/ \
(45) (*)
/ \
(*) (16)
/ \ / \
(*) (13)(*) (9)
/ \ / \
(5) (12) (a) (b)
案例4:树和森林之间的转换
问题描述
将一个森林转换为二叉树并对其进行遍历。
实现步骤
-
森林转换为二叉树:
- 将每棵树的兄弟节点作为其右子节点。
- 将每棵树的子树作为其左子节点。
-
使用二叉树遍历方法对森林进行操作。
实现代码
python
class ForestNode:
def __init__(self, value):
self.data = value
self.first_child = None
self.next_sibling = None
# 森林转换为二叉树
def forest_to_binary_tree(forest):
if not forest:
return None
root = TreeNode(forest.data)
root.left = forest_to_binary_tree(forest.first_child)
root.right = forest_to_binary_tree(forest.next_sibling)
return root
# 测试森林与二叉树转换
forest = ForestNode('A')
forest.first_child = ForestNode('B')
forest.first_child.next_sibling = ForestNode('C')
forest.first_child.first_child = ForestNode('D')
binary_tree = forest_to_binary_tree(forest)
print("Converted Binary Tree (Preorder): ", end="")
preorder(binary_tree)
输出
mathematica
Converted Binary Tree (Preorder): A B D C
5.9 小结
本章系统地介绍了树和二叉树的数据结构,重点包括:
- 树和二叉树的定义与性质。
- 二叉树的存储结构(顺序存储与链式存储)。
- 遍历二叉树(前序、中序、后序)及其实现。
- 哈夫曼树的构造与编码。
- 森林与二叉树的转换。
通过理论与实践结合,进一步加深了对树和二叉树应用的理解。这些内容在算法优化、文件压缩(如哈夫曼编码)等领域有广泛应用。