数据结构与算法【二叉搜索树】Java实现

 介绍

二叉搜索树(也称二叉排序树)是符合下面特征的二叉树:

  1. 树节点增加 key 属性,用来比较谁大谁小,key 不可以重复
  2. 对于任意一个树节点,它的 key 比左子树的 key 都大,同时也比右子树的 key 都小

查找、插入、删除的时间复杂度与树高相关

  • 如果这棵树左右平衡,那么时间复杂度均是 O(logN)
  • 这棵树如果左右高度相差过大,那么这时是最糟的情况,相当于线性查找。时间复杂度是 O(N)。

实现二叉搜索树

public class BSTree<K extends Comparable<K>, V> {public BSNode<K, V> root;static class BSNode<K, V> {K key;V value;BSNode<K, V> left;BSNode<K, V> right;public BSNode(K key) {this.key = key;}public BSNode(K key, V value) {this.key = key;this.value = value;}public BSNode(K key, V value, BSNode<K, V> left, BSNode<K, V> right) {this.key = key;this.value = value;this.left = left;this.right = right;}}
}

实现通过key获取值

public V get(K key) {BSNode<K, V> node = root;while (node != null) {//当传过来的key为null时会报错int result = key.compareTo(node.key);if (result < 0) {//说明key<node.keynode = node.left;} else if (result > 0) {node = node.right;} else {return node.value;}}return null;
}

实现获取最小key的值

任何节点的左孩子一定比该节点小,因此当遍历到没有左孩子时说明是key最小的节点

public V min() {if (root == null) {return null;}BSNode<K, V> node = root;while (node.left != null) {node = node.left;}return node.value;
}

实现获取最大key的值

任何节点的右孩子一定比该节点大,因此当遍历到没有右孩子时说明是key最大的节点

public V max() {if (root == null) {return null;}BSNode<K, V> node = root;while (node.right != null) {node = node.right;}return node.value;
}

实现新增节点

如果key在二叉搜索树中不存在时,进行新增,如果存在则进行值覆盖

public void put(K key, V value) {BSNode<K, V> node = root;BSNode<K, V> parent = null;int result = 0;while (node != null) {parent = node;result = key.compareTo(node.key);if (result < 0) {//说明key<node.keynode = node.left;} else if (result > 0) {node = node.right;} else {//如果存在该节点就覆盖node.value = value;return;}}//说明此时没有节点if (parent == null){root = new BSNode<K,V>(key,value);}else if (result<0){parent.left = new BSNode<K, V>(key, value);}else {parent.right = new BSNode<K, V>(key, value);}
}

实现获取前驱节点

最简单的实现方式,是进行一次中序遍历,这样可以直接到获取一个升序的排序结果。但是效率较差,因此我们采用别的实现方案。

找前驱节点,可以根据两个规律去实现

  • 节点有左子树,此时前驱节点就是左子树的最大值
  • 节点没有左子树,若离它最近的祖先自从左而来,此祖先即为前驱
public V predecessor(K key) {//祖先节点BSNode<K, V> ancestorFromLeft = null;BSNode<K, V> node = root;while (node != null) {int result = key.compareTo(node.key);if (result < 0) {node = node.left;} else if (result > 0) {//如果进入该分支,说明该节点可以作为一个祖先节点ancestorFromLeft = node;node = node.right;} else {break;}}if (node==null){//说明没有该节点return null;}//如果存在左孩子if (node.left!=null){return max(node.left);}//如果不存在左孩子return ancestorFromLeft!=null?ancestorFromLeft.value:null;
}

实现获取后驱节点

与获取前驱节点类似,也可以通过中序遍历拿到排序结果后,寻找指定节点的后驱节点。也可以通过以下两个规律来实现

  • 节点有右子树,此时后继节点即为右子树的最小值
  • 节点没有右子树,若离它最近的祖先自从右而来,此祖先即为后继
public V successor(K key){//祖先节点BSNode<K, V> ancestorFromLeft = null;BSNode<K, V> node = root;while (node != null) {int result = key.compareTo(node.key);if (result < 0) {//如果进入该分支,说明该节点可以作为一个祖先节点ancestorFromLeft = node;node = node.left;} else if (result > 0) {node = node.right;} else {break;}}if (node==null){//说明没有该节点return null;}//如果存在左孩子if (node.right!=null){return min(node.right);}//如果不存在左孩子return ancestorFromLeft!=null?ancestorFromLeft.value:null;
}

实现删除指定节点

删除节点比较麻烦。需要考虑4种情况

  • 被删除节点只存在左孩子
  • 被删除节点只存在右孩子
  • 被删除节点是根节点
  • 被删除节点存在两个孩子节点

前两种情况可以通过被删除节点的父结点来继承被删除节点的子节点来实现。而后两种情况则需要找到被删除节点的后驱节点来顶替被删除节点的位置

    public V delete(K key) {//找到被删除节点BSNode<K, V> deleteNode = root;//找到被删除节点的父结点BSNode<K, V> parent = null;while (deleteNode != null) {int result = key.compareTo(deleteNode.key);if (result < 0) {//说明key<node.keyparent = deleteNode;deleteNode = deleteNode.left;} else if (result > 0) {parent = deleteNode;deleteNode = deleteNode.right;} else {break;}}if (deleteNode == null) {//没找到该节点return null;}//当要删除的节点只存在左节点时if (deleteNode.right == null) {shift(parent, deleteNode, deleteNode.left);} else if (deleteNode.left == null) { //当要删除的节点只存在右节点时shift(parent, deleteNode, deleteNode.right);} else { //当要删除的节点存在两个子节点时,需要找到要删除节点的后驱节点//后驱节点BSNode<K, V> s = deleteNode.right;//后驱节点的父亲节点。用来判断被删除的节点与其后驱节点是否相邻BSNode<K, V> sParent = null;while (s.left != null) {sParent = s;s = s.left;}//如果要删除的节点与后驱节点不相邻if (sParent != deleteNode) {//将后驱节点的子节点交给后驱节点的父结点shift(sParent, s, s.right);//后驱节点接管被删除节点的右孩子s.right = deleteNode.right;}//如果要删除的节点与后驱节点相邻shift(parent,deleteNode,s);//后驱节点顶替被删除节点的位置s.left = deleteNode.left;}return deleteNode.value;}/*** 将要删除的节点的子节点转移到其父结点上** @param parent     父结点* @param deleteNode 要删除的节点* @param child      子节点*/private void shift(BSNode<K, V> parent, BSNode<K, V> deleteNode, BSNode<K, V> child) {if (parent == null) {//说明要删除的节点为根节点root = child;} else if (deleteNode.left == child) {//如果要删除的节点只存在左节点parent.left = child;}else {parent.right = child;}}

实现范围查询

采用中序遍历得到排序结果后,将符合范围的节点返回一个集合

//查找小于key的所有节点的value
public List<V> less(K key) {List<V> result = new ArrayList<>();LinkedList<BSNode<K, V>> linked = new LinkedList<>();BSNode<K, V> node = root;while (node != null || !linked.isEmpty()) {if (node != null) {linked.push(node);node = node.left;} else {BSNode<K, V> pop = linked.pop();int compare = key.compareTo(pop.key);//如果比指定值小if (compare > 0) {//加入列表result.add(pop.value);} else {//如果比指定值大,没有接着循环的必要break;}node = pop.right;}}return result;
}

可以自己实现一下>key或是k1<=values<=k2的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/150557.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL-对比两表中数据

需求&#xff1a;表1中有多行id相同 select a.item_code, totalAmount , b.online_qty from (select item_code, sum(a.online_qty) as TotalAmountfrom serial_bal awhere a.wh_code SJ38group by item_code) a left join(select item_code,online_qtyfrom wh_bal bwhere b.…

ESP32 MicroPython 图像采集及拍照功能的使用⑧

ESP32 MicroPython 图像采集及拍照功能的使用⑧ 1、摄像头应用2、图像采集5、实验结果6、按键拍照7、实验内容8、参考代码9、实验结果 1、摄像头应用 小车配有摄像头&#xff0c;可以使用摄像头实现拍照、图像显示、图像识别等功能。小车已经内置有我们专门移植的摄像头驱动库…

[Docker]六.Docker自动部署nodejs以及golang项目

一.自动部署nodejs 1.创建node项目相关文件 app.js代码如下: var express require(express);var appexpress();app.get(/,function(req,res){res.send(首页update); }) app.get(/news,function(req,res){res.send(首页); })//docker做端口映射的时候不要指定ip app.listen(30…

大模型重塑软件设计,南京真我加入飞桨技术伙伴,大模型生态圈成员又添一员!

为帮助伙伴更快、更好的应用大模型技术&#xff0c;飞桨技术伙伴体系及权益基于星河共创计划全面升级&#xff0c;通过丰富的场景、技术、算力、品牌等资源&#xff0c;为伙伴企业提供一站式的大模型资源对接&#xff0c;全面降低创建AI原生应用的门槛。 近日&#xff0c;南京…

二十三种设计模式全面解析-解密职责链模式:请求处理的设计艺术

当我们构建软件系统时&#xff0c;经常会遇到需要处理各种不同类型请求的情况。有时&#xff0c;请求的处理逻辑可能相当复杂&#xff0c;需要按照一定的规则和条件进行处理。在本文中&#xff0c;我们将深入探讨职责链模式在请求处理中的应用。职责链模式通过将请求发送者和接…

win10家庭版系统远通过一根网线程连接另一台机器

用网线连接两个机器 打开cmd命令行 输入ipconfig&#xff0c;查看 复制 IPv4地址 打开 远程桌面 程序 点击连接 输入在另外一机器设置好的用户名和密码即可

docker打包chatpdf(自写)

docker打包上传 docker build -t kitelff/chatpdf:v0.1 .##修改镜像名字 docker tag c2c1a0eb4e08 kitelff/chatpdf:v0.1## push docker push kitelff/chatpdf:v0.1上传文件&#xff0c;测试效果

【brpc学习实战二】brpc client构建基本流程

client基本概念及学习指南 https://github.com/luozesong/brpc/blob/master/docs/cn/client.md 一、编写proto 这里与服务一致&#xff0c;实际开发中需要双端共同确定proto内容&#xff1b; 二、初始化channel rpc channel可以视为socket编程中的client对象 定义一个chan…

字符串统计

题目部分 题目字符串统计难度易题目说明给定两个字符集合&#xff0c;一个是全量字符集&#xff0c;一个是已占用字符集&#xff0c;已占用字符集中的字符不能再使用&#xff0c;要求输出剩余可用字符集。输入描述1. 输入一个字符串 一定包含&#xff0c;前为全量字符集 后的为…

BananaPi BPI-M6(Raspberry Pi 5) Android 平板电脑镜像测试温度

我已经在本文中介绍了 全新的Banana Pi BPI-M6&#xff0c;并讨论了其与Raspberry Pi 5的硬件特性比较。 然后我将 Android 平板电脑固件上传到 eMMC&#xff0c;从而使 Banana Pi 实际可用。一开始有点坎坷&#xff0c;但文章中有更多内容。 在另一台电脑上&#xff0c;一切都…

Arcgis小技巧【16】:ArcMap的那些功能在ArcGIS Pro里都去哪儿了?

有部分小伙伴现在已经用上了ArcGIS Pro&#xff0c;但可能还会有些不习惯。 一个很重要的原因&#xff0c;原来在ArcMap中的一些功能&#xff0c;好像在Pro里消失了。 不排除一些功能确实被移除了&#xff0c;但大部分其实是因为UI的变化&#xff0c;给放在了别的地方。 这里…

Linux CentOS7配置网络参数

CentOS6及以前版本中主要使用ifconfig工具&#xff0c;查看、配置网络参数。后来对推荐使用ip命令查看配置网络参数。而centos7中&#xff0c;不再赞成使用ifconfig工具&#xff0c;取而代之的是nmcli工具&#xff0c;服务管理也是以systemctl工具取代了service,这些之前版本的…

音视频项目—基于FFmpeg和SDL的音视频播放器解析(十二)

介绍 在本系列&#xff0c;我打算花大篇幅讲解我的 gitee 项目音视频播放器&#xff0c;在这个项目&#xff0c;您可以学到音视频解封装&#xff0c;解码&#xff0c;SDL渲染相关的知识。您对源代码感兴趣的话&#xff0c;请查看基于FFmpeg和SDL的音视频播放器 如果您不理解本…

学霸教你自学人工智能

在这个信息爆炸的时代&#xff0c;人工智能已经渗透到我们生活的方方面面。无论是语音助手、自动驾驶汽车&#xff0c;还是医疗诊断&#xff0c;人工智能都在发挥着越来越重要的作用。如果你对人工智能充满热情&#xff0c;希望在这个领域有所建树&#xff0c;那么&#xff0c;…

基于深度学习的视觉三维重建研究总结

参考连接&#xff1a; 基于深度学习的视觉三维重建研究总结 - 知乎

微信私域运营工具CRM

为什么要做微信私域&#xff1f; 客户在哪里&#xff1f;微信&#xff01;在中国&#xff0c;不论男女老少&#xff0c;90%的人每天使用微信至少5次&#xff0c;每次使用时间超过90分钟&#xff0c;已经成为像吃饭穿衣一样的生活必需品。因此&#xff0c;我们的目标客户就在微…

【github】初学者使用指南

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于新西兰奥克兰大学攻读IT硕士学位。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。跨领域…

简单选择排序

目录 基本原理举例示例代码总结简单选择排序 VS 堆排序简单选择排序 VS 冒泡排序 本文主要介绍简单选择排序的基本原理、具体例子&#xff0c;以及代码实现。 基本原理 简单选择排序的基本原理是&#xff1a; 在未排序序列中找到最小&#xff08;大&#xff09;元素&#xf…

一个奇怪的蓝牙模块分析记录

蓝牙标识PZ-BT11 从这个蓝牙通电后的表现可以看到有2个蓝牙&#xff0c;其中一个带有BLE标识&#xff0c;可能是一个双模蓝牙 首先这不是一个普通的JDY蓝牙&#xff0c; 因为普通JDY蓝牙只有1个蓝牙信号&#xff08;从手机搜索蓝牙&#xff09; 这可能是一个BLE蓝牙 因为B…

VMware——WindowServer2012R2环境mysql5.7.14解压版安装主从复制(图解版)

目录 一、服务器信息二、192.168.132.33主服务器上安装mysql&#xff08;主&#xff09;2.1、环境变量配置2.2、安装2.2.1、修改配置文件内容2.2.2、初始化mysql并指定超级用户密码2.2.3、安装mysql服务2.2.4、启动mysql服务2.2.5、登录用户管理及密码修改2.2.6、开启远程访问 …