第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 小结
本章系统地介绍了树和二叉树的数据结构,重点包括:
- 树和二叉树的定义与性质。
 - 二叉树的存储结构(顺序存储与链式存储)。
 - 遍历二叉树(前序、中序、后序)及其实现。
 - 哈夫曼树的构造与编码。
 - 森林与二叉树的转换。
 
通过理论与实践结合,进一步加深了对树和二叉树应用的理解。这些内容在算法优化、文件压缩(如哈夫曼编码)等领域有广泛应用。