news 2026/4/15 20:10:51

二叉搜索树、二叉排序树(查找、插入和删除)——Java版本

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
二叉搜索树、二叉排序树(查找、插入和删除)——Java版本

1. 概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树
  • 不允许存在相同的结点

如:一个int a [] = {5,3,4,1,7,8,2,6,0,9}; 数组组成二叉排序树

定义二叉搜索树的结构:

static class TreeNode { public int val; public TreeNode left; public TreeNode right; public TreeNode(int val) { this.val = val; } } public TreeNode root;

2. 操作 - 查找

/* 搜索 搜索的时间复杂度最好:O(logN) 搜索的时间复杂度最坏:O(N) */ public boolean search(int key){ TreeNode cur = root; while(cur != null){ //找到key,返回true if(cur.val == key) { return true; } //到右树寻找 else if(cur.val < key){ cur = cur.right; } //到左树寻找 else{ cur = cur.left; } } //没有找到值为key的结点 return false; }

3. 操作 - 插入

(1) 如果树为空树,即根 == null,直接插入

(2)如果树不是空树,按照查找逻辑确定插入位置,插入新结点

/* 插入:都是插入到叶子结点 插入的时间复杂度最好:O(logN) 插入的时间复杂度最坏:O(N) */ public void insert(int val) throws Exception { TreeNode newNode = new TreeNode(val); if (root == null) { root = newNode; return; } TreeNode cur = root; TreeNode parent = null; while (cur != null) { parent = cur; //判断是否有重复元素进入 if (cur.val == val) { throw new Exception("有重复元素进入!"); } //如果要插入的结点的值,大于当前结点,就应该在cur的右子树 if (cur.val < val) { cur = cur.right; } //如果要插入的结点的值,小于当前结点,就应该在cur的左子树 else if (cur.val > val) { cur = cur.left; } } //如果要插入的结点小于父节点,就应该接在父节点的左子树 if (parent.val > val) { parent.left = newNode; } //如果要插入的结点大于父节点,就应该接在父节点的右子树 else { parent.right = newNode; } }

搜索和插入的运行结果:

4. 操作 - 删除 ⭐⭐⭐⭐⭐

分情况讨论,如下所示:

设待删除结点为 cur, 待删除结点的双亲结点为 parent

(1)cur.left == null

① cur 是 root,则root = cur.right

② cur 不是 root,cur 是 parent.left,则parent.left = cur.right

③ cur 不是 root,cur 是 parent.right,则parent.right = cur.right

(2)cur.right == null

① cur 是 root,则root = cur.left

② cur 不是 root,cur 是 parent.left,则parent.left = cur.left

③ cur 不是 root,cur 是 parent.right,则parent.right = cur.left

(3)cur.left != null && cur.right != null

需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被 删除节点中,再来处理该结点的删除问题。

这部分的代码为(有bug):

//3.cur两边的子树都不为空 else{ TreeNode t = cur.left; TreeNode tp = null; while(t.right != null){ tp = t; t = t.right; } cur.val = t.val; //这个就变成了删除t指向的结点,且t结点的右子树为空 tp.right = t.left; }

原来的图:

第一步进行分析和改进:

第二步进行分析和改进:

树类的方法:

//删除结点的操作 public void remove(int val) { TreeNode parent = null; TreeNode cur = root; while (cur != null) { //去右子树寻找 if (cur.val < val) { parent = cur; cur = cur.right; } else if (cur.val > val) { parent = cur; cur = cur.left; } //找到了 else { removeNode(cur, parent); return; } } } private void removeNode(TreeNode cur, TreeNode parent) { //1.cur的左子树为空 if (cur.left == null) { //(1)cur 是 root,则 root = cur.right if (cur == root) { root = cur.right; } //(2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.right else if (cur == parent.left) { parent.left = cur.right; } //(3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.right else { parent.right = cur.right; } } //2.cur的右子树为空 else if (cur.right == null) { //(1)cur 是 root,则 root = cur.left if (cur == root) { root = cur.left; } //(2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.left else if (cur == parent.left) { parent.left = cur.right; } //(3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.left else { parent.right = cur.right; } } //3.cur两边的子树都不为空 else { TreeNode t = cur.left; TreeNode tp = cur; while (t.right != null) { tp = t; t = t.right; } cur.val = t.val; if (cur == tp) { tp.left = t.left; } else { //这个就变成了删除t指向的结点,且t结点的右子树为空 tp.right = t.left; } } }

测试类:

public class Test { public static void main(String[] args) throws Exception { BinarySearchTree binarySearchTree = new BinarySearchTree(); binarySearchTree.insert(5); binarySearchTree.insert(3); binarySearchTree.insert(7); binarySearchTree.insert(1); binarySearchTree.insert(4); binarySearchTree.insert(6); binarySearchTree.insert(8); binarySearchTree.insert(0); binarySearchTree.insert(9); binarySearchTree.remove(3); } }

5. 二叉搜索树的完整代码

public class BinarySearchTree { static class TreeNode { public int val; public TreeNode left; public TreeNode right; public TreeNode(int val) { this.val = val; } } public TreeNode root; /* 搜索 搜索的时间复杂度最好:O(logN) 搜索的时间复杂度最坏:O(N) */ public boolean search(int key) { TreeNode cur = root; while (cur != null) { //找到key,返回true if (cur.val == key) { return true; } //到右树寻找 else if (cur.val < key) { cur = cur.right; } //到左树寻找 else { cur = cur.left; } } //没有找到值为key的结点 return false; } /* 插入:都是插入到叶子结点 插入的时间复杂度最好:O(logN) 插入的时间复杂度最坏:O(N) */ public void insert(int val) throws Exception { TreeNode newNode = new TreeNode(val); if (root == null) { root = newNode; return; } TreeNode cur = root; TreeNode parent = null; while (cur != null) { parent = cur; //判断是否有重复元素进入 if (cur.val == val) { throw new Exception("有重复元素进入!"); } //如果要插入的结点的值,大于当前结点,就应该在cur的右子树 if (cur.val < val) { cur = cur.right; } //如果要插入的结点的值,小于当前结点,就应该在cur的左子树 else if (cur.val > val) { cur = cur.left; } } //如果要插入的结点小于父节点,就应该接在父节点的左子树 if (parent.val > val) { parent.left = newNode; } //如果要插入的结点大于父节点,就应该接在父节点的右子树 else { parent.right = newNode; } } //删除结点的操作 public void remove(int val) { TreeNode parent = null; TreeNode cur = root; while (cur != null) { //去右子树寻找 if (cur.val < val) { parent = cur; cur = cur.right; } else if (cur.val > val) { parent = cur; cur = cur.left; } //找到了 else { removeNode(cur, parent); return; } } } private void removeNode(TreeNode cur, TreeNode parent) { //1.cur的左子树为空 if (cur.left == null) { //(1)cur 是 root,则 root = cur.right if (cur == root) { root = cur.right; } //(2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.right else if (cur == parent.left) { parent.left = cur.right; } //(3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.right else { parent.right = cur.right; } } //2.cur的右子树为空 else if (cur.right == null) { //(1)cur 是 root,则 root = cur.left if (cur == root) { root = cur.left; } //(2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.left else if (cur == parent.left) { parent.left = cur.right; } //(3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.left else { parent.right = cur.right; } } //3.cur两边的子树都不为空 else { TreeNode t = cur.left; TreeNode tp = cur; while (t.right != null) { tp = t; t = t.right; } cur.val = t.val; if (cur == tp) { tp.left = t.left; } else { //这个就变成了删除t指向的结点,且t结点的右子树为空 tp.right = t.left; } } } }

6. 性能分析

插入和删除操作都必须先查找查找效率代表了二叉搜索树中各个操作的性能。 对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度 的函数,即结点越深,则比较次数越多。 但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

  • 最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:
  • 最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N

问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,都可以,是二叉搜索树的性能最佳?

答:可以,这就涉及到后面的AVL树和红黑树了,后期的文章会继续讨论AVL树和红黑树。

7. 和Java类集的关系

TreeMap 和 TreeSet 即 Java 中利用搜索树实现的 Map 和 Set;实际上用的是红黑树,而红黑树是一棵近似平衡的二叉搜索树,即在二叉搜索树的基础之上 +颜色以及红黑树性质验证,关于红黑树的内容后序再进行讲解。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 15:16:01

告别Pyppeteer安装烦恼:手动下载Chromium并指定路径的保姆级教程

离线部署Pyppeteer全攻略&#xff1a;手动下载与配置Chromium的终极方案 当你在内网环境或网络受限的场景下使用Pyppeteer时&#xff0c;自动下载Chromium的步骤往往会成为拦路虎。本文将带你彻底解决这个痛点&#xff0c;通过手动下载和配置Chromium&#xff0c;让你在任何环境…

作者头像 李华
网站建设 2026/4/14 4:07:09

plog终极指南:1000行代码打造便携式C++日志库

plog终极指南&#xff1a;1000行代码打造便携式C日志库 【免费下载链接】plog Portable, simple and extensible C logging library 项目地址: https://gitcode.com/gh_mirrors/pl/plog plog是一款轻量级、可移植且高度可扩展的C日志库&#xff0c;仅用1000行核心代码即…

作者头像 李华
网站建设 2026/4/15 15:28:14

交期延误?轻流 AI 无代码给出新解法

交期延误&#xff1f;轻流 AI 无代码给出新解法早上 8 点&#xff0c;生产例会上&#xff0c;生产经理再次被问到&#xff1a;"昨天的计划为什么又没完成&#xff1f;"这已经是本周第三次了。计划赶不上变化、进度不透明、延期率高——这些问题像三座大山&#xff0c…

作者头像 李华
网站建设 2026/4/15 4:18:46

vLLM-v0.17.1惊艳效果:AWQ量化后Llama3-8B显存占用降至11GB

vLLM-v0.17.1惊艳效果&#xff1a;AWQ量化后Llama3-8B显存占用降至11GB 1. vLLM框架简介 vLLM是一个专为大型语言模型(LLM)设计的高性能推理和服务库&#xff0c;以其出色的速度和易用性著称。这个项目最初由加州大学伯克利分校的天空计算实验室开发&#xff0c;现在已经发展…

作者头像 李华
网站建设 2026/4/15 5:45:53

5分钟掌握sakura.css暗色模式:打造现代网站的终极视觉体验

5分钟掌握sakura.css暗色模式&#xff1a;打造现代网站的终极视觉体验 【免费下载链接】sakura :cherry_blossom: a minimal css framework/theme. 项目地址: https://gitcode.com/gh_mirrors/sa/sakura sakura.css是一款极简的CSS框架&#xff0c;它提供了优雅的暗色模…

作者头像 李华
网站建设 2026/4/14 3:58:48

终极解决ImagePicker常见问题:从崩溃到功能异常的完整指南

终极解决ImagePicker常见问题&#xff1a;从崩溃到功能异常的完整指南 【免费下载链接】ImagePicker 完全仿微信的图片选择&#xff0c;并且提供了多种图片加载接口&#xff0c;选择图片后可以旋转&#xff0c;可以裁剪成矩形或圆形&#xff0c;可以配置各种其他的参数 项目地…

作者头像 李华