【数据结构与算法:五、树和二叉树】

第5章 树和二叉树

5.1 树和二叉树的定义

5.1.1 树的定义

树(Tree) 是一种常见的数据结构,用于描述具有层次结构的关系。树由一个根节点和若干子树组成,每个子树又可以递归地看作是一棵树。

树的特点:

  1. 树是一个有限的节点集合,包含一个根节点(唯一入口)。
  2. 除根节点外,每个节点都有且仅有一个父节点。
  3. 每个节点可以有零个或多个子节点,子节点之间互不相交。

树的表示:

  • 图示:
mathematica 复制代码
          A
        / | \
       B  C  D
      / \
     E   F

5.1.2 树的基本术语

  1. 节点(Node): 树中的基本元素。
  2. 根节点(Root): 没有父节点的节点(如上图的 A)。
  3. 父节点(Parent): 一个节点的直接上级节点。
  4. 子节点(Child): 一个节点的直接下级节点。
  5. 叶节点(Leaf): 没有子节点的节点(如 E、F、C、D)。
  6. 节点的度(Degree): 一个节点的子节点数量。
  7. 树的度(Degree of Tree): 树中所有节点度的最大值。
  8. 层次(Level): 从根节点开始计算节点的深度,第 1 层是根节点,第 2 层是根的子节点,以此类推。
  9. 高度(Height): 树中节点的最大层次。
  10. 路径(Path): 从一个节点到另一个节点的路线。

5.1.3 二叉树的定义

二叉树(Binary Tree) 是一种特殊的树结构,每个节点最多有两个子节点,分别为左子节点右子节点

二叉树的特点:

  1. 每个节点最多有两个子节点,分别称为左子节点和右子节点。
  2. 二叉树是一种递归结构,每个子树也都是一棵二叉树。

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. 性质1: 二叉树的第 i 层最多有 2^(i-1) 个节点。
  2. 性质2: 深度为 k 的二叉树最多有 2^k - 1 个节点。
  3. 性质3: 对于任何二叉树,叶子节点数 n0 和度为2的节点数 n2 满足关系:n0 = n2 + 1。
  4. 性质4: 完全二叉树的节点可以用数组顺序存储,且节点索引具有以下性质:
    • 索引为 i 的节点,其左子节点索引为 2i,右子节点索引为 2i+1,父节点索引为 floor(i/2)。

5.4.2 二叉树的存储结构

  1. 顺序存储:
  • 使用数组存储二叉树的节点。
  • 索引规则:
    • 2i 表示左子节点;
    • 2i+1 表示右子节点;
    • floor(i/2) 表示父节点。
  • 示例:完全二叉树:
css 复制代码
        A
       / \
      B   C
     /
    D

存储为:[A, B, C, D, null, null, null]

  1. 链式存储:
  • 每个节点用结构体或类存储,包括数据域和左右子节点指针。

    python 复制代码
    class TreeNode:
        def __init__(self, value):
            self.data = value
            self.left = None
            self.right = None

5.5 遍历二叉树和线索二叉树

5.5.1 遍历二叉树

  1. 前序遍历(PreOrder): 根 -> 左 -> 右。

    python 复制代码
    def preorder(node):
        if node:
            print(node.data)
            preorder(node.left)
            preorder(node.right)
  2. 中序遍历(InOrder): 左 -> 根 -> 右。

    python 复制代码
    def inorder(node):
        if node:
            inorder(node.left)
            print(node.data)
            inorder(node.right)
  3. 后序遍历(PostOrder): 左 -> 右 -> 根。

    python 复制代码
    def postorder(node):
        if node:
            postorder(node.left)
            postorder(node.right)
            print(node.data)

5.5.2 线索二叉树

线索二叉树 使用空指针存储前驱和后继信息,便于遍历。

5.6 树和森林

  1. 树的存储结构:

    • 双亲表示法
    • 孩子表示法
    • 孩子兄弟表示法
  2. 森林与二叉树转换:

    • 将森林中的每棵树转换为二叉树,并通过右指针连接兄弟节点。

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. 案例1:用链式存储构造二叉树
  2. 案例2:实现并演示二叉树的前序、中序、后序遍历
  3. 案例3:构造哈夫曼树并生成哈夫曼编码
  4. 案例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:实现并演示二叉树的前序、中序、后序遍历

问题描述

使用递归实现二叉树的三种遍历方式:

  1. 前序遍历(根 -> 左 -> 右)
  2. 中序遍历(左 -> 根 -> 右)
  3. 后序遍历(左 -> 右 -> 根)
实现代码
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:树和森林之间的转换

问题描述

将一个森林转换为二叉树并对其进行遍历。

实现步骤
  1. 森林转换为二叉树:

    • 将每棵树的兄弟节点作为其右子节点。
    • 将每棵树的子树作为其左子节点。
  2. 使用二叉树遍历方法对森林进行操作。

实现代码
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 小结

本章系统地介绍了树和二叉树的数据结构,重点包括:

  1. 树和二叉树的定义与性质。
  2. 二叉树的存储结构(顺序存储与链式存储)。
  3. 遍历二叉树(前序、中序、后序)及其实现。
  4. 哈夫曼树的构造与编码。
  5. 森林与二叉树的转换。

通过理论与实践结合,进一步加深了对树和二叉树应用的理解。这些内容在算法优化、文件压缩(如哈夫曼编码)等领域有广泛应用。

相关推荐
低调的JVM19 分钟前
IPV6离线地址库Java版(极致性能,无内存分配,申请了专利)
java·ip
lilu888888821 分钟前
DevOps流水线中的AI自动化实践——ScriptEcho的赋能
前端·人工智能·自动化·devops
孙尚香蕉21 分钟前
ZooKeeper Java API操作
java·zookeeper·java-zookeeper
cooldream200926 分钟前
数据库模型全解析:从文档存储到搜索引擎
数据库·人工智能·搜索引擎·知识图谱
tt55555555555527 分钟前
多链表合并
数据结构·链表
Together_CZ1 小时前
BloombergGPT: A Large Language Model for Finance——面向金融领域的大语言模型
人工智能·语言模型·金融·finance·bloomberggpt·面向金融领域的大语言模型·金融大模型
asyxchenchong8881 小时前
基于R语言的DICE模型实践技术应用
人工智能
AI大模型learner1 小时前
探索Whisper:从原理到实际应用的解析
人工智能·深度学习·机器学习
Linux520小飞鱼2 小时前
F#语言的网络编程
开发语言·后端·golang
weixin_399264292 小时前
QT c++ 样式 设置 标签(QLabel)的渐变色美化
开发语言·c++·qt