Skip to content

algo04 树与二叉树 — exercise.py 练习指南

Download exercise.py

练习目标

通过四个任务掌握树的遍历、BST 操作、AVL 旋转和哈夫曼编码。

任务清单

任务1:迭代版后序遍历

技巧:后序(左-右-根)= 反序(根-右-左)。用一个栈先做根-右-左,然后反转。

标准后序: 4, 5, 2, 3, 1
根右左:   1, 3, 2, 5, 4
→ 反转即可!

任务2:BST 插入和查找

insert: 从根出发,根据比较结果左转或右转,直到找到空位。 search: 同样逻辑,找到匹配值或抵达 None。

任务3:AVL 旋转

右旋(LL):y 是失衡节点,x = y.leftT3 = x.right。旋转后 x 成为新的根。

左旋(RR):x 是失衡节点,y = x.rightT2 = y.left。旋转后 y 成为新的根。

任务4(Bonus):哈夫曼编解码

编码:频率统计 → 优先队列合并 → DFS 生成编码表 → 逐字符编码。 解码:反转编码表,从编码串头部开始匹配。

验证

bash
cd algo04_tree_binarytree/code
python exercise.py

预期全部通过,并展示哈夫曼编码的压缩效果。

完整代码

py
# -*- coding: utf-8 -*-
"""
===============================================================================
algo04_tree_binarytree/code/exercise.py — 树与二叉树练习
===============================================================================
练习目标:
  1. 实现二叉树的中序和后序遍历(迭代版)
  2. 实现 BST 的插入和查找操作
  3. 实现 AVL 树的右旋和左旋操作
  4. 实现哈夫曼编码的完整编码/解码

运行方式:python exercise.py
===============================================================================
"""


class TreeNode:
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None


# ============================================================================
# 任务 1:迭代版后序遍历
# ============================================================================

def postorder_iterative(root):
    """后序遍历(迭代)—— 使用栈

    思路:使用两个栈,或者用一个栈 + 记录 last_visited
    技巧:后序遍历的逆序 = 根→右→左 的逆序
    """
    # TODO: 实现迭代版后序遍历
    if not root:
        return []
    result = []
    stack = [root]
    # 先做 根→右→左 的遍历,然后反转
    while stack:
        node = stack.pop()
        result.append(node.val)
        if node.left:
            stack.append(node.left)
        if node.right:
            stack.append(node.right)
    # 反转得到 左→右→根
    return result[::-1]


# ============================================================================
# 任务 2:实现 BST 的插入和查找
# ============================================================================

class BST:
    def __init__(self):
        self.root = None

    # TODO: 实现 insert 方法(迭代版)
    def insert(self, val):
        if not self.root:
            self.root = TreeNode(val)
            return
        cur = self.root
        while True:
            if val < cur.val:
                if not cur.left:
                    cur.left = TreeNode(val)
                    return
                cur = cur.left
            else:
                if not cur.right:
                    cur.right = TreeNode(val)
                    return
                cur = cur.right

    # TODO: 实现 search 方法
    def search(self, val):
        cur = self.root
        while cur:
            if val == cur.val:
                return True
            elif val < cur.val:
                cur = cur.left
            else:
                cur = cur.right
        return False

    def inorder(self):
        result = []
        def dfs(node):
            if not node: return
            dfs(node.left)
            result.append(node.val)
            dfs(node.right)
        dfs(self.root)
        return result


# ============================================================================
# 任务 3(Bonus):AVL 树的旋转
# ============================================================================

class AVLNode:
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None
        self.height = 1


def get_height(node):
    return node.height if node else 0


def update_height(node):
    node.height = 1 + max(get_height(node.left), get_height(node.right))


# TODO: 实现右旋(LL 旋转)
def rotate_right(y):
    r"""
       y              x
      / \            / \
     x   T4   ->    z    y
    / \                / \
   z   T3            T3  T4
   图示:y 的平衡因子 = 2,x 的平衡因子 = 1(LL 情况)
   """
    # TODO: 你的代码
    x = y.left
    T3 = x.right
    x.right = y
    y.left = T3
    update_height(y)
    update_height(x)
    return x


# TODO: 实现左旋(RR 旋转)
def rotate_left(x):
    r"""
   x                y
  / \              / \
 T1  y     ->     x    z
    / \         / \
   T2  z       T1 T2
   图示:x 的平衡因子 = -2,y 的平衡因子 = -1(RR 情况)
   """
    # TODO: 你的代码
    y = x.right
    T2 = y.left
    y.left = x
    x.right = T2
    update_height(x)
    update_height(y)
    return y


# ============================================================================
# 任务 4:哈夫曼编解码
# ============================================================================

import heapq


class HuffmanNode:
    def __init__(self, char, freq):
        self.char = char
        self.freq = freq
        self.left = None
        self.right = None

    def __lt__(self, other):
        return self.freq < other.freq


def huffman_encode(text):
    """哈夫曼编码:返回 (编码后的二进制串, 编码表)"""
    # 统计频率
    freq = {}
    for ch in text:
        freq[ch] = freq.get(ch, 0) + 1

    # 构建哈夫曼树
    # TODO: 你的代码 —— 使用优先队列合并节点
    heap = [HuffmanNode(ch, f) for ch, f in freq.items()]
    heapq.heapify(heap)
    while len(heap) > 1:
        left = heapq.heappop(heap)
        right = heapq.heappop(heap)
        merged = HuffmanNode(None, left.freq + right.freq)
        merged.left = left
        merged.right = right
        heapq.heappush(heap, merged)

    root = heap[0]

    # 生成编码表
    # TODO: 你的代码 —— DFS 遍历哈夫曼树生成 0/1 编码
    codes = {}
    def dfs(node, code):
        if not node: return
        if node.char is not None:
            codes[node.char] = code
            return
        dfs(node.left, code + '0')
        dfs(node.right, code + '1')
    dfs(root, '')

    # 编码
    encoded = ''.join(codes[ch] for ch in text)
    return encoded, codes


def huffman_decode(encoded, codes):
    """哈夫曼解码 —— 根据编码表还原原文"""
    # 反转编码表:code → char
    # TODO: 你的代码
    reverse_codes = {code: char for char, code in codes.items()}
    result = []
    current = ''
    for bit in encoded:
        current += bit
        if current in reverse_codes:
            result.append(reverse_codes[current])
            current = ''
    return ''.join(result)


# ============================================================================
# 主函数
# ============================================================================

def main():
    print('=' * 60)
    print('  树与二叉树 — 练习程序')
    print('=' * 60)

    # 测试任务 1
    print('\n--- 任务 1:迭代版后序遍历 ---')
    root = TreeNode(1)
    root.left = TreeNode(2); root.right = TreeNode(3)
    root.left.left = TreeNode(4); root.left.right = TreeNode(5)
    post = postorder_iterative(root)
    print(f'后序: {post}')
    assert post == [4, 5, 2, 3, 1]
    print('✅ 任务 1 通过!')

    # 测试任务 2
    print('\n--- 任务 2:BST ---')
    bst = BST()
    for v in [5, 3, 7, 2, 4, 6, 8]:
        bst.insert(v)
    print(f'BST 中序: {bst.inorder()}')
    assert bst.search(4) and bst.search(7) and not bst.search(99)
    print('✅ 任务 2 通过!')

    # 测试任务 3
    print('\n--- 任务 3:AVL 旋转 ---')
    # 构建 LL 情况
    y = AVLNode(30); y.left = AVLNode(20); y.left.left = AVLNode(10)
    y.height = 3; y.left.height = 2
    new_root = rotate_right(y)
    print(f'右旋后: root={new_root.val}, left={new_root.left.val}, right={new_root.right.val}')
    assert new_root.val == 20
    print('✅ 任务 3 通过!')

    # 测试任务 4
    print('\n--- 任务 4:哈夫曼编解码 ---')
    text = "ABRACADABRA"
    encoded, codes = huffman_encode(text)
    decoded = huffman_decode(encoded, codes)
    print(f'原文: {text}')
    print(f'编码表: {codes}')
    print(f'编码: {encoded} (长度: {len(encoded)} bits)')
    print(f'解码: {decoded}')
    assert decoded == text
    print(f'原始大小: {len(text) * 8} bits, 压缩后: {len(encoded)} bits, 压缩率: {len(encoded)/(len(text)*8)*100:.1f}%')
    print('✅ 任务 4 通过!')

    print('\n🎉 所有练习已完成!')


if __name__ == '__main__':
    main()