数据结构-树

二叉树基础

二叉树是数据结构中非常重要的一种非线性结构,它以层次化的方式存储数据,在查找、排序等场景中有着广泛应用。本文将结合具体代码实现,详细介绍二叉树的基本概念、操作及相关知识点。

一、二叉树的基本概念

二叉树是由 n(n≥0)个节点组成的有限集合,每个节点最多有两个子节点,分别称为左子节点和右子节点。特殊的二叉树包括:

  • 空二叉树:节点数为 0 的二叉树
  • 满二叉树:所有叶子节点都在同一层,且非叶子节点都有两个子节点
  • 完全二叉树:除最后一层外,其他层的节点数都达到最大值,且最后一层的节点都靠左排列

二、二叉搜索树(BST)的实现

我们实现的BinaryTree类本质上是一棵二叉搜索树,它具有以下特性:左子树的所有节点值小于根节点值,右子树的所有节点值大于等于根节点值。

1. 节点类(Node)

节点是二叉树的基本组成单元,包含节点值和左右子节点的引用:

java 复制代码
package 二叉树;

public class Node {
    public int value;
    public Node left;  // 左子节点
    public Node right; // 右子节点

    public Node(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Node [value=" + value + ", left=" + left + ", right=" + right + "]";
    }
}

2. 二叉树核心操作

(1)插入操作

插入操作是构建二叉搜索树的基础,根据二叉搜索树的特性,新节点会被放置在正确的位置:

java 复制代码
// 插入节点
public void insert(int num) {
    Node node = new Node(num);
    if(root == null) {  // 树为空时,新节点作为根节点
        root = node;
        return;
    }
    Node index = root;
    while(true) {
        if(index.value > num) {  // 小于当前节点,向左子树查找
            if(index.left == null) {
                index.left = node;
                return;
            }
            index = index.left;
        } else {  // 大于等于当前节点,向右子树查找
            if(index.right == null) {
                index.right = node;
                return;
            }
            index = index.right;
        }
    }
}
(2)遍历操作

遍历是二叉树最常用的操作,主要有深度优先遍历和广度优先遍历两大类:

  • 先序遍历:根节点 → 左子树 → 右子树
java 复制代码
public void beforeOrder(Node node) {
    if(node == null) return;
    System.out.println(node.value);  // 访问根节点
    beforeOrder(node.left);          // 遍历左子树
    beforeOrder(node.right);         // 遍历右子树
}
  • 中序遍历:左子树 → 根节点 → 右子树(对二叉搜索树进行中序遍历可得到有序序列)
java 复制代码
public void inOrder(Node node) {
    if(node == null) return;
    inOrder(node.left);              // 遍历左子树
    System.out.println(node.value);  // 访问根节点
    inOrder(node.right);             // 遍历右子树
}
  • 后序遍历:左子树 → 右子树 → 根节点
java 复制代码
public void afterOrder(Node node) {
    if(node == null) return;
    afterOrder(node.left);           // 遍历左子树
    afterOrder(node.right);          // 遍历右子树
    System.out.println(node.value);  // 访问根节点
}
  • 广度优先遍历(层序遍历):按层次依次访问各节点,使用队列实现
java 复制代码
public void levelOrder() {
    Queue<Node> queue = new LinkedList<Node>();
    queue.add(root);
    while(!queue.isEmpty()) {
        Node index = queue.poll();
        System.out.println(index.value);  // 访问当前节点
        if(index.left != null) {
            queue.add(index.left);        // 左子节点入队
        }
        if(index.right != null) {
            queue.add(index.right);       // 右子节点入队
        }
    }
}

三、代码测试与运行

Test类用于验证二叉树的功能:

java 复制代码
public class Test {
    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree();
        // 插入节点构建二叉树
        tree.insert(5);
        tree.insert(10);
        tree.insert(3);
        tree.insert(1);
        tree.insert(4);
        tree.insert(11);
        tree.insert(8);
        
        System.out.println("树的结构:");
        System.out.println(tree);  // 打印树的结构
        
        System.out.println("层序遍历结果:");
        tree.levelOrder();  // 执行层序遍历
    }
}

四、二叉树重要知识点

  1. 二叉树的高度与深度

    • 节点的深度:从根节点到该节点的路径长度
    • 节点的高度:从该节点到最深叶子节点的路径长度
    • 树的高度:根节点的高度
  2. 二叉搜索树的时间复杂度

    • 插入、查找、删除操作的平均时间复杂度为 O (log n)
    • 在最坏情况下(树退化为链表),时间复杂度为 O (n)
  3. 平衡二叉树

    • 为解决二叉搜索树在最坏情况下的性能问题,出现了平衡二叉树(如 AVL 树、红黑树)
    • 平衡二叉树通过旋转操作维持树的平衡性,保证操作的时间复杂度稳定在 O (log n)
  4. 二叉树的应用场景

    • 数据库索引(B 树、B + 树)
    • 表达式解析(表达式树)
    • Huffman 编码(用于数据压缩)
    • 决策树(机器学习领域)
  5. 删除操作要点

    • 删除叶子节点:直接删除
    • 删除只有一个子节点的节点:用子节点替代被删除节点
    • 删除有两个子节点的节点:找到中序后继(右子树中最小节点)或中序前驱(左子树中最大节点)替代,再删除该后继 / 前驱节点