数据结构与算法——17.二叉搜索树

这篇文章我们来看一下数据结构中的二叉搜索树。

目录

1.概述

2.二叉搜索树的实现

3.总结


1.概述

我们前面学到的数据结构,比如:动态数组、链表、队列、栈、堆,这些数据结构存储完数据后,我们要去查找某个数据,它的时间复杂度是O(n),因为这些数据结构的底层实现都是数组或者链表,都是线性的。我们前面有学过二分查找,它的最优时间复杂度为O(lngn)。下面,我们来学习另外一种便于查找的数据结构——二叉搜索树

二叉搜索树:又被称为二叉查找树。其特点如下:

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

下面看一张图:

二叉搜索树的理想查找时间复杂度为O(logn)

2.二叉搜索树的实现

下面来看一下二叉搜索树的实现:

二叉搜索树的根据key值找节点值,找最大,找最小,找前驱和后继都是比较简单的,思路都是很好理解的。

下面重点来讲一下删除的思路(删除的情况很多):

  1. 删除节点没有左孩子,将右孩子托孤给Parent
  2. 删除节点没有右孩子,将左孩于托孤给Parent
  3. 删除节点左右孩子都没有,已经被涵盖在情况1、情况2当中,把null 托孤给Parent
  4. 删除节点左右孩子都有,可以将它的后继节点(称为S)托孤给Parent,再称S的父亲为SP,又分两种情况:(1)SP就是被删除节点,此时D与S紧邻,只需将S托孤给Parent (2)SP不是被删除节点,此时D与S不相邻,此时需要将S的后代托孤给SP,再将S托孤给Parent

下面来看一下代码的具体实现(代码太长,就不截图展示了):

package Tree;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;/**二叉搜索树*/
public class L2_BSTree1<T extends Comparable<T>> {/**节点类*/static class BSTNode<T>{T key;Object value;BSTNode left;BSTNode right;public BSTNode(T key) {this.key = key;}public BSTNode(T key, Object value) {this.key = key;this.value = value;}public BSTNode(T key, Object value, BSTNode left, BSTNode right) {this.key = key;this.value = value;this.left = left;this.right = right;}}BSTNode<T> root;//根节点/**根据key值得到节点的值*/public Object get(T key){BSTNode<T> node = root;while (node!=null){/*** 该值比传入参数大,返回1* 该值比传入参数小,返回-1* 该值等于传入参数,返回0* */int result = key.compareTo(node.key);if (result < 0){node = node.left;}else if (result > 0){node = node.right;}else {return node.value;}}return null;}public Object get1(T key){return doGet(root,key);}private Object doGet(BSTNode<T> node, T key){//递归的函数int result = key.compareTo(node.key);if (node == null){return null;}if (result < 0){return doGet(node.left,key);//向左找}else if (result > 0){return doGet(node.right,key);//向左找}else{return node.value;//返回当前的值}}/**得到最小key值所对应的值*/public Object min(){//非递归版return max(root);}public Object min(BSTNode node){//非递归版if (node == null){return null;}BSTNode pre = node;while (pre.left != null){pre = pre.left;}return pre.value;}public Object min1(){//递归版return doMin(root);}private Object doMin(BSTNode node){if (node == null){return null;}if (node.left == null){return node.value;}return doMin(node.left);}/**得到最大key值所对应的值*/public Object max(){//非递归版return max(root);}private Object max(BSTNode<T> node){if (node == null){return null;}BSTNode pre = node;while (pre.right != null){pre = pre.right;}return pre.value;}public Object max1(){//递归版return doMax(root);}private Object doMax(BSTNode node){if (node == null){return null;}if (node.right == null){return node.value;}return doMin(node.right);}/**存储key值和节点值*/public void put(T key,Object value){//1.如果key存在,更新操作//1.如果key不存在,新增操作BSTNode<T> node = root;BSTNode<T> parent = null;//记录key的前一个值while (node != null){parent = node;int result = key.compareTo(node.key);if (result < 0){node = node.left;}else if (result > 0){node = node.right;}else {//找到了node.value = value;return;}}//没找到,新增if (parent == null){root = new BSTNode<T>(key,value);}int result = key.compareTo(parent.key);if (result < 0){parent.left = new BSTNode<T>(key,value);}else if(result > 0){parent.right = new BSTNode<T>(key,value);}}/**找到某一个key的前驱值*/public Object predecessor(T key){BSTNode<T> p = root;BSTNode<T> ancestorFromLeft = null;while (p != null){int result = key.compareTo(p.key);if (result < 0){p = p.left;}else if (result > 0){ancestorFromLeft = p;p = p.right;}else {break;}}if (p == null){//没找到节点的情况return null;}if (p.left != null){//找到节点,有左子树return max(p.left);}return ancestorFromLeft != null ?ancestorFromLeft.value :null;}/**找到某一个key的后继值*/public Object successor(T key){BSTNode<T> p = root;BSTNode<T> ancestorFromRight = null;while (p != null){int result = key.compareTo(p.key);if (result < 0){ancestorFromRight = p;p = p.left;}else if (result > 0){p = p.right;}else {break;}}if (p == null){//没找到节点的情况return null;}if (p.right != null){//找到节点,有左子树return min(p.right);}return ancestorFromRight != null ?ancestorFromRight.value :null;}/**根据key值删除对应的节点*/public Object delete(T key){BSTNode<T> p = root;BSTNode<T> parent = null;while (p != null){int result = key.compareTo(p.key);if (result < 0){parent = p;//记录当前节点的父节点p = p.left;}else if (result > 0){parent = p;p = p.right;}else {break;}}if (p == null){return null;}//删除操作if (p.left == null ){//情况1shift(parent,p,p.right);} else if(p.right == null ){//情况2shift(parent,p,p.left);} else {//情况4BSTNode<T> s = p.right;BSTNode<T> sParent = p;//后继结点的父亲while (s.left != null){sParent = s;s = s.left;}if (sParent != p){//不相邻shift(sParent,s,s.right);s.right = p.right;}shift(parent ,p,s);s.left = p.left;}return p.value;}/*** 托孤方法* parent:被删除节点的父亲* deleted:被删除节点* child:被上去的结点* */private void shift(BSTNode<T> parent,BSTNode<T> deleted,BSTNode<T> child){if (parent == null){root = child;} else if (deleted == parent.left){parent.left = child;}else {parent.right = child;}}public Object delete1(T key){ArrayList<Object> Aresult = new ArrayList<>();//保存被删除节点的值root = doDelete(root,key,Aresult);return Aresult.isEmpty()? null:Aresult.get(0);}private BSTNode<T> doDelete(BSTNode<T> node,T key,ArrayList<Object> Aresult){//node:递归删除的起点//返回值:删剩下的孩子节点if (node == null){return null;}int result = key.compareTo(node.key);if (result < 0){node.left = doDelete(node.left,key,Aresult);return node;}if (result > 0){node.right = doDelete(node.right,key,Aresult);return node;}Aresult.add(node.value);if (node.left == null){return node.right;}if(node.right == null){return node.left;}BSTNode<T> s = node.right;while (s.left != null){s = s.left;}s.right = doDelete(node.right,s.key,new ArrayList<>());s.left = node.left;return s;}/*找比指定key小的所有节点的value值*/public List<Object> less(T key){ArrayList<Object> list = new ArrayList<>();BSTNode<T> p = root;LinkedList<BSTNode<T>> stack = new LinkedList<>();while (p != null || !stack.isEmpty()){if (p != null){stack.push(p);p = p.left;}else {BSTNode<T> pop = stack.pop();int result = key.compareTo( pop.key);if (result < 0 ){list.add(pop.value);} else {break;}p = pop.right;}}return list;}/*找比指定key小的所有节点的value值*/public List<Object> greater(T key){ArrayList<Object> list = new ArrayList<>();BSTNode<T> p = root;LinkedList<BSTNode<T>> stack = new LinkedList<>();while (p != null || !stack.isEmpty()){if (p != null){stack.push(p);p = p.left;}else {BSTNode<T> pop = stack.pop();int result = key.compareTo( pop.key);if (result > 0 ){list.add(pop.value);}p = pop.right;}}return list;}/*找比指定key小的所有节点的value值*/public List<Object> between(T key1,T key2){ArrayList<Object> list = new ArrayList<>();BSTNode<T> p = root;LinkedList<BSTNode<T>> stack = new LinkedList<>();while (p != null || !stack.isEmpty()){if (p != null){stack.push(p);p = p.left;}else {BSTNode<T> pop = stack.pop();int result1 = key1.compareTo( pop.key);int result2 = key2.compareTo( pop.key);if (result1 > 0 && result2 < 0){list.add(pop.value);}else if (result2 > 0){break;}p = pop.right;}}return list;}}

3.总结

怎么说呢,二叉搜索树对比前面的二叉树来说,难度确实是上了一个档次。但是,越学数据结构与算法你越会有这样一种感觉:他们的套路都大差不差!二叉搜索树是用链表来实现的,只要心中有图,多画画图,然后熟悉链表的一些操作,熟悉一些循环流程的判断,那么那些操作都能实现出来。如果实现不了,那就再多结合其他的数据结构来想一想。链表的操作主要就是看一些循环流程的控制。其余的没啥难的。对于数组,数组的一些操作要比链表难,因为数组太死了。

我之前的代码的注释比较多,因为刚接触,不熟悉,但现在代码中的注释并不多,那是因为一些操作都写了很多遍了。虽然不至于能默写下来,但是可以自己推导着写出来。思路有了,也练了几遍手,那么再遇见这个问题自己就能推导了。所以数据结构与算法学到后面主要就是学思路了。

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

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

相关文章

JavaScript Web APIs第一天笔记

复习&#xff1a; splice() 方法用于添加或删除数组中的元素。 **注意&#xff1a;**这种方法会改变原始数组。 删除数组&#xff1a; splice(起始位置&#xff0c; 删除的个数) 比如&#xff1a;1 let arr [red, green, blue] arr.splice(1,1) // 删除green元素 consol…

基于SpringBoot的微服务在线教育系统设计与实现

目录 前言 一、技术栈 二、系统功能介绍 用户管理 课程信息管理 学科管理 职业规划管理 我的笔记管理 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本…

百度统计配置详细图文教程包含siteId、百度统计AccessToken、百度统计代码获取步骤教程

一、前言 很多网友开发者都不知道百度统计siteId、百度统计token怎么获取&#xff0c;在网上找的教程都是几年前老的教程&#xff0c;因此给大家出一期详细百度统计siteId、百度统计token、百度统计代码获取详细步骤教程。 二、登录到百度统计 1.1 登录到百度统计官网 使用个…

wsl2 更新报错问题解决记录

1、问题 win10 中安装的 wsl2&#xff0c;启动 docker desktop 时提示 wsl2 有问题&#xff1a; 于是点击推荐的地址连接到微软&#xff0c;下载 wsl2 的更新文件。之后运行&#xff0c;又报错&#xff1a; 更新被卡住。 2、解决方法 WinR 输入 cmd 打开命令行窗口&#x…

FL Studio21.1电脑试用体验版音乐制作软件

我一直以来对音乐艺术都很感兴趣。最近我接触到了一款名为 FL Studio 的电脑版音乐制作软件&#xff0c;深感其强大功能和广泛适用性。通过使用这款软件&#xff0c;我不仅深入了解了音乐制作的过程与技巧&#xff0c;也加深了对音乐创作的理解。 FL Studio 最初是一款针对 MI…

国庆节,我们用代码来画个国旗~

转载自&#xff1a;国庆节&#xff0c;我们用代码来画个国旗&#xff5e; - 知乎 (zhihu.com) 如果你刚好闲的无聊&#xff0c;想想以前每次放假&#xff0c;本来放假前想好出去干啥干啥的&#xff0c;最后都是窝在家里敲代码了。可能很多人还是如此&#xff0c;那么怎样给这一…

【设计模式】备忘录模式

文章目录 1.备忘录模式定义2.备忘录模式的角色3.备忘录模式实现3.1.场景说明3.2.结构类图3.3.代码实现 4.备忘录模式优缺点5.备忘录模式适用场景6.备忘录模式总结 主页传送门&#xff1a;&#x1f481; 传送 1.备忘录模式定义 备忘录&#xff08;Memento Pattern&#xff09;模…

OCI 发布了容器运行时和镜像规范!

7 月 19 日是开放容器计划Open Container Initiative&#xff08;OCI&#xff09;的一个重要里程碑&#xff0c;OCI 发布了容器运行时和镜像规范的 1.0 版本&#xff0c;而 Docker 在这过去两年中一直充当着推动和引领的核心角色。 我们的目标是为社区、客户以及更广泛的容器行…

QT配置FFmpeg出现错误原因

文章目录 QT配置ffmpeg出现&#xff1a; undefined reference to "avcodec_version"没有配置环境变量QT和FFmpeg的版本不对应直接添加FFmpeg的头文件没有在.pro文件添加路径 QT 程序异常退出没有在debug文件里面存放dll库 QT配置ffmpeg出现&#xff1a; undefined re…

华为小型智能园区网络解决方案

云时代来袭&#xff0c;数字化正在从园区办公延伸到生产和运营的方方面面&#xff0c;智慧校园&#xff0c;柔性制造&#xff0c;掌上金融和电子政务等&#xff0c;面对各种各样的新兴业态的涌现&#xff0c;企业需要构建一张无所不联、随心体验、业务永续的全无线网络&#xf…

现代卷积网络实战系列4:PyTorch从零构建VGGNet训练MNIST数据集

&#x1f308;&#x1f308;&#x1f308;现代卷积网络实战系列 总目录 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 1、MNIST数据集处理、加载、网络初始化、测试函数 2、训练函数、PyTorch构建LeNet网络 3、PyTorch从零构建AlexNet训练MNIST数据…

性能测试工具 — JMeter

一、JMeter准备工作 1、JMeter介绍 Apache JMeter 应用程序是开源软件&#xff0c;是一个 100% 纯 Java 应用程序。用于测试Web应用程序、API和其他网络协议的性能。它具有以下特点&#xff1a; 1. 开源免费&#xff1a;JMeter是Apache软件基金会下的一个开源项目&#xff0…