轻松掌握数据结构:二叉查找树

二叉搜索树

又叫二叉查找树,BST树

定义

左子树所有节点的值小于根节点,右子树所有节点的值大于根节点(左 < 根 < 右),且左右子树也是这样。

特点

  • 支持高效查找(类似二分查找)(因为当遍历到一个节点的时候,先判断目标值同节点值的大小,然后确定进行左子节点的遍历还是右子节点的遍历,相当于少遍历了一半的数据 ),所以时间复杂度度==O (logn)==

  • 从左到右遍历(中序遍历)可得到一个有序的序列。

  • ==BST通常不允许重复的值==

缺点:

当依次插入的数据都比上一个数据值大时,所有的节点就会依次堆到右子节点,此时树就成了一个链表的结构了,查询时间复杂度就降为了==O(n)==

代码实现:

java 复制代码
package com.stu.shujujiegou;

/**
 * @title:
 * @auther: eleven
 * @description:
 * @data: 2025/8/5 16:45
 * @parm:
 * @return:
 */
import java.util.ArrayList;
import java.util.List;

public class BinarySearchTree {
    // 二叉搜索树节点类
    private static class TreeNode {
        int val;
        TreeNode left;  // 左子节点
        TreeNode right; // 右子节点

        public TreeNode(int val) {
            this.val = val;
            this.left = null;
            this.right = null;
        }
    }

    private TreeNode root; // 根节点

    // 构造空树
    public BinarySearchTree() {
        root = null;
    }

    /**
     * 插入元素
     * @param val 要插入的值
     */
    public void insert(int val) {
        root = insertRecursive(root, val);
    }

    // 递归插入
    private TreeNode insertRecursive(TreeNode current, int val) {
        // 如果当前节点为空,创建新节点作为插入位置
        if (current == null) {
            return new TreeNode(val);
        }

        // 根据值的大小决定插入左子树还是右子树
        if (val < current.val) {
            current.left = insertRecursive(current.left, val);
        } else if (val > current.val) {
            current.right = insertRecursive(current.right, val);
        }
        // 相等的值不插入(BST通常不允许重复值)

        return current;
    }

    /**
     * 查找元素
     * @param val 要查找的值
     * @return 找到返回true,否则返回false
     */
    public boolean contains(int val) {
        return containsRecursive(root, val);
    }

    // 递归查找
    private boolean containsRecursive(TreeNode current, int val) {
        // 节点为空,未找到
        if (current == null) {
            return false;
        }

        // 找到匹配的值
        if (val == current.val) {
            return true;
        }

        // 小于当前节点值,去左子树查找;否则去右子树查找
        return val < current.val
                ? containsRecursive(current.left, val)
                : containsRecursive(current.right, val);
    }

    /**
     * 删除元素
     * @param val 要删除的值
     * @return 删除成功返回true,否则返回false
     */
    public boolean delete(int val) {
        // 如果树为空,直接返回false
        if (root == null) {
            return false;
        }

        // 记录删除前是否包含该值
        boolean exists = contains(val);
        if (exists) {
            root = deleteRecursive(root, val);
        }
        return exists;
    }

    // 递归删除
    private TreeNode deleteRecursive(TreeNode current, int val) {
        if (current == null) {
            return null;
        }

        // 1. 查找要删除的节点
        if (val < current.val) {
            current.left = deleteRecursive(current.left, val);
        } else if (val > current.val) {
            current.right = deleteRecursive(current.right, val);
        } else {
            // 2. 找到要删除的节点,处理三种情况

            // 情况1:叶子节点(没有子节点)
            if (current.left == null && current.right == null) {
                return null;
            }

            // 情况2:只有一个子节点
            if (current.left == null) {
                return current.right; // 返回右子节点
            }
            if (current.right == null) {
                return current.left;  // 返回左子节点
            }

            // 情况3:有两个子节点
            // 因为二叉搜索树 数据有序满足 左<中<右,所以我们删除当前节点后,需要找到右子树的最小节点替换到当前位置
            // 找到右子树中的最小值节点
            TreeNode smallestNode = findSmallest(current.right);
            // 用最小值替换当前节点值
            current.val = smallestNode.val;
            // 删除右子树中的最小值节点
            current.right = deleteRecursive(current.right, smallestNode.val);
        }

        return current;
    }

    // 查找最小节点(最左节点)
    private TreeNode findSmallest(TreeNode node) {
        return node.left == null ? node : findSmallest(node.left);
    }

    // 查找最大节点(最右节点)
    private TreeNode findLargest(TreeNode node) {
        return node.right == null ? node : findLargest(node.right);
    }

    /**
     * 获取最小值
     * @return 树中的最小值
     */
    public int findMin() {
        if (root == null) {
            throw new IllegalStateException("树为空,无法查找最小值");
        }
        return findSmallest(root).val;
    }

    /**
     * 获取最大值
     * @return 树中的最大值
     */
    public int findMax() {
        if (root == null) {
            throw new IllegalStateException("树为空,无法查找最大值");
        }
        return findLargest(root).val;
    }

    /**
     * 前序遍历(根 -> 左 -> 右)
     * @return 遍历结果列表
     */
    public List<Integer> preOrder() {
        List<Integer> result = new ArrayList<>();
        preOrderRecursive(root, result);
        return result;
    }

    private void preOrderRecursive(TreeNode node, List<Integer> result) {
        if (node != null) {
            result.add(node.val);          // 访问根节点
            preOrderRecursive(node.left, result);  // 遍历左子树
            preOrderRecursive(node.right, result); // 遍历右子树
        }
    }

    /**
     * 中序遍历(左 -> 根 -> 右)
     * 对于BST,中序遍历结果是升序的
     * @return 遍历结果列表
     */
    public List<Integer> inOrder() {
        List<Integer> result = new ArrayList<>();
        inOrderRecursive(root, result);
        return result;
    }

    private void inOrderRecursive(TreeNode node, List<Integer> result) {
        if (node != null) {
            inOrderRecursive(node.left, result);   // 遍历左子树
            result.add(node.val);          // 访问根节点
            inOrderRecursive(node.right, result);  // 遍历右子树
        }
    }

    /**
     * 后序遍历(左 -> 右 -> 根)
     * @return 遍历结果列表
     */
    public List<Integer> postOrder() {
        List<Integer> result = new ArrayList<>();
        postOrderRecursive(root, result);
        return result;
    }

    private void postOrderRecursive(TreeNode node, List<Integer> result) {
        if (node != null) {
            postOrderRecursive(node.left, result);  // 遍历左子树
            postOrderRecursive(node.right, result); // 遍历右子树
            result.add(node.val);          // 访问根节点
        }
    }

    /**
     * 层序遍历(按层次从上到下,从左到右)
     * @return 遍历结果列表
     */
    public List<Integer> levelOrder() {
        List<Integer> result = new ArrayList<>();
        if (root == null) {
            return result;
        }

        // 使用队列实现层序遍历
        java.util.Queue<TreeNode> queue = new java.util.LinkedList<>();
        queue.add(root);

        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            result.add(node.val);

            // 左子节点入队
            if (node.left != null) {
                queue.add(node.left);
            }
            // 右子节点入队
            if (node.right != null) {
                queue.add(node.right);
            }
        }
        return result;
    }

    /**
     * 清空树
     */
    public void clear() {
        root = null;
    }

    /**
     * 检查树是否为空
     * @return 空返回true,否则返回false
     */
    public boolean isEmpty() {
        return root == null;
    }

    // 测试方法
    public static void main(String[] args) {
        BinarySearchTree bst = new BinarySearchTree();

        // 插入元素
        bst.insert(50);
        bst.insert(30);
        bst.insert(70);
        bst.insert(20);
        bst.insert(40);
        bst.insert(60);
        bst.insert(80);

        System.out.println("中序遍历(升序): " + bst.inOrder());
        // 输出: [20, 30, 40, 50, 60, 70, 80]

        System.out.println("前序遍历: " + bst.preOrder());
        // 输出: [50, 30, 20, 40, 70, 60, 80]

        System.out.println("后序遍历: " + bst.postOrder());
        // 输出: [20, 40, 30, 60, 80, 70, 50]

        System.out.println("层序遍历: " + bst.levelOrder());
        // 输出: [50, 30, 70, 20, 40, 60, 80]

        System.out.println("最小值: " + bst.findMin());  // 输出: 20
        System.out.println("最大值: " + bst.findMax());  // 输出: 80
        System.out.println("是否包含60: " + bst.contains(60));  // 输出: true
        System.out.println("是否包含90: " + bst.contains(90));  // 输出: false

        // 删除元素
        bst.delete(30);
        System.out.println("删除30后的中序遍历: " + bst.inOrder());
        // 输出: [20, 40, 50, 60, 70, 80]

        bst.delete(50);
        System.out.println("删除50后的中序遍历: " + bst.inOrder());
        // 输出: [20, 40, 60, 70, 80]
    }
}

重点看数据插入和数据删除

数据插入:

java 复制代码
/**
 * 插入元素
 * @param val 要插入的值
 */
public void insert(int val) {
    root = insertRecursive(root, val);
}

// 递归插入
private TreeNode insertRecursive(TreeNode current, int val) {
    // 如果当前节点为空,创建新节点作为插入位置
    if (current == null) {
        return new TreeNode(val);
    }

    // 根据值的大小决定插入到左子树还是右子树
    if (val < current.val) {
        current.left = insertRecursive(current.left, val);
    } else if (val > current.val) {
        current.right = insertRecursive(current.right, val);
    }
    // 相等的值不插入(BST通常不允许重复值)
    return current;
}

数据删除:

java 复制代码
/**
 * 删除元素
 * @param val 要删除的值
 * @return 删除成功返回true,否则返回false
 */
public boolean delete(int val) {
    // 如果树为空,直接返回false
    if (root == null) {
        return false;
    }

    // 记录删除前是否包含该值
    boolean exists = contains(val);
    if (exists) {
        root = deleteRecursive(root, val);
    }
    return exists;
}

// 递归删除
private TreeNode deleteRecursive(TreeNode current, int val) {
    if (current == null) {
        return null;
    }

    // 1. 查找要删除的节点
    if (val < current.val) {
        current.left = deleteRecursive(current.left, val);
    } else if (val > current.val) {
        current.right = deleteRecursive(current.right, val);
    } else {
        // 2. 找到要删除的节点,处理三种情况
        // 情况1:叶子节点(没有子节点)
        if (current.left == null && current.right == null) {
            return null;
        }

        // 情况2:只有一个子节点
        if (current.left == null) {
            return current.right; // 返回右子节点
        }
        if (current.right == null) {
            return current.left;  // 返回左子节点
        }

        // 情况3:有两个子节点
        // 因为二叉搜索树 数据有序且满足 左<中<右,所以我们删除当前节点后,需要找到右子树的最小节点替换到当前位置
        // 找到右子树中的最小值节点
        TreeNode smallestNode = findSmallest(current.right);
        // 用最小值替换当前节点值
        current.val = smallestNode.val;
        // 删除右子树中的最小值节点
        current.right = deleteRecursive(current.right, smallestNode.val);
    }

    return current;
}

对应代码中==情况2==,只有一个子节点:

对应代码中==情况3==:被删除节点有两个子节点:

相关推荐
该用户已不存在13 分钟前
OpenJDK、Temurin、GraalVM...到底该装哪个?
java·后端
怀刃34 分钟前
内存监控对应解决方案
后端
西工程小巴39 分钟前
实践笔记-VSCode与IDE同步问题解决指南;程序总是进入中断服务程序。
c语言·算法·嵌入式
码事漫谈1 小时前
VS Code Copilot 内联聊天与提示词技巧指南
后端
Tina学编程1 小时前
48Days-Day19 | ISBN号,kotori和迷宫,矩阵最长递增路径
java·算法
Moonbit1 小时前
MoonBit Perals Vol.06: MoonBit 与 LLVM 共舞 (上):编译前端实现
后端·算法·编程语言
Moonbit1 小时前
MoonBit Perals Vol.06: MoonBit 与 LLVM 共舞(下):llvm IR 代码生成
后端·程序员·代码规范
Moonbit1 小时前
MoonBit Pearls Vol.05: 函数式里的依赖注入:Reader Monad
后端·rust·编程语言
小奋斗1 小时前
深入浅出:ES5/ES6+数组扁平化详解
javascript·面试
bobz9652 小时前
ThanosRuler
后端