数据结构之二叉搜索树(TreeSetTreeMap)

目录

一.搜索树

1.1概念

1.2适用场景

 2.二叉搜索树的基本操作

2.1二叉搜索树的定义

2.2查找

2.1.1基本思路

2.3插入

2.3.1基本思路

2.4删除

2.4.1基本思路

2.5遍历

 2.6性能分析

二.TreeSet

Map和Set

1.概念

2.模型

1.定义 

2.基本操作

三.TreeMap

1.定义

 2.基本操作

 总结:


一.搜索树

1.1概念

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

  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树

注意:二叉搜索树中的节点不可重复出现

例子:下图中,这棵二叉树显然满足上面的性质,这是棵二叉搜索树。

不是二叉搜索树的: 

 

1.2适用场景

二叉搜索树有着高效的插入、搜索、删除操作

 2.二叉搜索树的基本操作

二叉搜索树有以下基本操作:

  1. 查找(search):在搜索树中查找特定的节点。
  2. 插入(insert):在树中找到满足搜索树性质的位置插入新的节点
  3. 删除(detele):从树中删除指定的节点
  4. 遍历(traverse):二叉搜索树的遍历,有前中后遍历,中序遍历是有序的

2.1二叉搜索树的定义

public class BinarySea {class TreeNode {int val;TreeNode left;TreeNode right;public TreeNode(int x) {this.val = x;}}public TreeNode root;//作为二叉搜素树的根节点
}

2.2查找

假设现在已经有了一棵上图中一棵二叉搜索树,如果要在这颗二叉搜索树中查找相应的键值是否存在,如何进行?(假设查找值为key)

2.1.1基本思路
  1. 判断二叉搜索树是否为空,若为空,返回false
  2. 遍历二叉搜索树,判断键值(key)和根节点值的大小,若根节点大于key进入左子树小于key,进入右子树,若刚好等于根节点的值,则返回true
  3. 重复上述操作,当遍历完二叉树,没有找到,返回false

代码实现:

  public  boolean search(int key){//若二叉搜索树为空,返回falseif(root==null){return false;}//不为空,借助变量进行搜索TreeNode cur=root;while (cur!=null){//如果当前节点的值大于key,则向左子树搜索if(cur.val>key){cur=cur.left;}//如果当前节点的值小于key,则向右子树搜索else if(cur.val<key){cur=cur.right;}//如果当前节点的值等于key,则返回trueelse{return true;}}//如果搜索结束,没有找到,则返回falsereturn false;}

对于二叉搜索树的查找操作,其时间复杂度:

  1. 在最好情况下(刚好是根节点):O(1)
  2. 在最坏情况下(所有节点只有左节点或者只有右子节点):O(n)
  3. 平均复杂度:O(logn) (n是树中的节点数量,在每次比较后,搜索空间都会减半)

2.3插入

2.3.1基本思路
  1. 在插入前需要判断二叉搜索树是否为空,若为空,则将以key为值的节点作为根节点,并返回true。
  2. 若前面条件满足,那么就需要进行判断,判断的条件与查找操作基本相同。当cur的值大于key,那么就进入cur的左子树;若cur的值小于key,则进入cur的右子树;若cur的值和key值相等,说明键值已经存在,插入失败,返回false。插入操作,需要借助一个变量prev,来保存每次cur的父节点。(假设cur=root(cur是要进行判断),prev为cur的父节点。)

假设要插入的节点为8:(以上图作基础)

当找到要插入节点的父节点后,需要判断插入节点的值和父节点prev的值大小,很明显,6<8,所以要插入的节点在prev的右子树。

可以得到:

代码实现:

    public boolean insert(int key){if(root==null){//如果二叉搜索树为空,则直接插入root=new TreeNode(key);return  true;}//二叉搜索树不为空,进行插入操作TreeNode cur=root;TreeNode prev=null;while (cur!=null){if(cur.val>key) {//如果当前节点的值大于key,则向左子树搜索prev = cur;cur = cur.left;} else if (cur.val<key) {//如果当前节点的值小于key,则向右子树搜索prev = cur;cur = cur.right;}else{//如果当前节点的值等于key,则返回falsereturn  false;}}//创建节点TreeNode node=new TreeNode(key);//判断节点与prev值的大小关系if(prev.val>key) {//如果节点值小于prev,则插入到左子树prev.left = node;}else {//如果节点值大于prev,则插入到右子树prev.right = node;}return true;}

对于插入操作,其时间复杂度:

  1. 在最好情况下:O(logn)
  2. 最坏情况下:O(n)
  3. 平均时间复杂度:O(logn)

2.4删除

2.4.1基本思路

对于想要删除的位置的节点,我们需要考虑三种情况:(假设想要删除节点为key,其父节点为parent)

  1. key.left==null

               1.key刚好是root,则root=key.right;

               2.key不是root,key是parent.left,那么parent.left=key.right;

               3.key不是root,key是parent.right,那么parent.right=key.right.

     2.key.right==null

              1.key刚好是root,则root=key.left;

               2.key不是root,key是parent.left,那么parent.left=key.left;

               3.key不是root,key是parent.right,那么parent.right=key.left.

     3.key.left!=null&&key.right!=null

               1.需要用替换法进行删除,即在它的右子树中寻找中序遍历中的第一个节点(关键值最小的),用它的值填补到被删除节点中,再处理该节点的删除问题。(或者也可以在待删除节点的左子树中寻找中序遍历的最后一个节点,即左子树中的最大值,填补到待删除节点中,再处理删除问题。)

               

以下图为例:假设要删除的节点是10,先进行查找,找到待删除节点的位置,和其父节点。

找到之后,需要在其左子树找最大值或者右子树中找最小值进行替换。

这里定义变量target=cur.right,targetParent=null(用来保留target的父节点),寻找待删除节点的右子树,让target往左子树走(每次走的时候,targetParent都要保留此时target,再让其往左子树走),当target的左子树为空时,说明此时已经找到右子树中的最小值,将target的值填补到cur,再让targetParent.left=target.left。此时,删除完成。

代码实现:

    /*** 删除二叉搜索树中指定值的节点* @param key 需要删除的节点值*/public void remove(int key){if(root==null){// 树为空,无需删除return;}TreeNode cur=root;TreeNode prev=null;while(cur!=null){if(cur.val<key){// 向右子树搜索prev=cur;cur=cur.right;}else if(cur.val>key){// 向左子树搜索prev=cur;cur=cur.left;}else{// 找到需要删除的节点,调用removeNode进行删除removeNode(prev,cur);}}}/*** 实际删除指定节点的操作* @param parent 被删除节点的父节点* @param cur 需要删除的节点*/public void removeNode(TreeNode parent,TreeNode cur){if(cur.left==null){// 删除节点没有左子树的情况if(cur==root){root=cur.right;} else if (parent.left==cur) {parent.left=cur.right;} else{parent.right=cur.right;}} else if (cur.right==null) {// 删除节点没有右子树的情况if(cur==root){root=cur.left;} else if (parent.left==cur) {parent.left=cur.left;} else{parent.right=cur.left;}}else{// 删除节点既有左子树又有右子树的情况,找到右子树中最左的节点替代当前节点,再删除该最左节点TreeNode target=cur.right;TreeNode targetParent=cur;while(target.left!=null){targetParent=target;target=target.left;}cur.val=target.val;if(target==targetParent.left){targetParent.left=target.right;}else{targetParent.right=target.right;}}}

2.5遍历

对于二叉搜索树树的遍历,其遍历与二叉树相同

二叉树​​​​​​

我们以中序遍历为例:

 //搜索树的中序遍历public void inOrder(TreeNode root) {if(root==null){return;}//遍历左子树inOrder(root.left);System.out.print(root.val+" ");//遍历右子树inOrder(root.right);}

 2.6性能分析

对于二叉搜索树,在插入和删除操作之前都必须进行查找,查找效率代表了二叉搜索树中的各个操作的性能。

对于有n个节点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是节点在二叉搜索树的深度的函数,即节点越深,则比较次数越多。

对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树,如图:

最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log2N

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

二.TreeSet

Map和Set

1.概念

Map和Set是一种专门用来进行搜索的容器或数据结构,其搜索的小了与其具体的实例化子类有关。Map和Set是一种适合动态查找的容器。

2.模型

把搜索的数据称为关键字(key),和关键字对应的称为值(value),将其称为Key—value的键值对,有两种模型:

  1. 纯key模型:

例如:有个英语词典,查找某个单词是否在词典中出现

           在微信的好友中,查找某个人的名字,是否存在

  1. Key-value模型:

 例如:统计一个字符串中,某个字符与其字符串中出现的次数:<字符,出现次数>.

Map中存储的就是key-value的键值对,而Set只存储了key。

1.定义 

TreeSet是java集合框架中的一种类,实现了Set接口,提供了一种有序且不允许有重复元素的集合。TreeSet内部实现基于红黑树,这是一种自平衡的二叉查找树,它保证了插入、删除和查找操作的高效性,通常这些操作的时间复杂度为O(log n)。

2.基本操作

测试:

import java.util.Set;
import java.util.TreeSet;
import java.util.Iterator;public class test {public static void main(String[] args) {Set<String> s=new TreeSet<>();s.add("孙悟空");s.add("猪八戒");s.add("唐僧");//遍历for(String str:s){System.out.print(str+" ");}System.out.println();//查找System.out.println(s.contains("孙悟空"));//判断有多少个元素System.out.println(s.size());//判断集合是否为空System.out.println(s.isEmpty());//删除s.remove("孙悟空");//利用迭代器Iterator<String> it=s.iterator();while(it.hasNext()){System.out.print(it.next()+" ");}}
}

下面是有关Set 的文档

集 (Java Platform SE 8 ) (oracle.com)

 注意:

  1. Set是继承自Collection的一个接口类
  2. 唯一性:Set中值存储key,并且要求key一定要唯一
  3. 有序性:在向TreeSet中添加元素时,会根据元素的自然顺序(如果元素实现了Comparable接口)或提供的Comparator来确定该元素在树中的位置。
  4. TreeSet的底层是使用Mao实现的,其使用key与Obiect的一个默认对象作为键值插入到Map中的
  5. Set的最大功能就是对集合中的元素进行去重
  6. Set中的key不能修改,若要修改,只能将原来的删除,再重新进行插入
  7. TreeSet中不能插入null的key,HashSet可以
  8. 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序
                             Set 底层结构                                                           TreeSet                           
                                底层结构                                   红黑树

                    插入 / 删除 / 查找时间

                                复杂度

                                   O(log N)
                               是否有序                                 关于 Key 有序
                               线程安全                                    不安全
                               比较与覆写

               key 必须能够比较,否则会抛出

                     ClassCastException 异常

                               入/删除/查找区别               按照红黑树的特性来进行插入和删除
                               应用场景                           需要 Key 有序场景下

三.TreeMap

1.定义

TreeMap是Java集合框架中的一部分,它实现了SortedMap接口,用于存储键值对(Key-Value对),并能按照键的自然顺序或者自定义比较器(Comparator)排序。TreeMap的底层实现是一个红黑树数据结构,这保证了其高效的查找、插入和删除操作,尤其是查找操作的时间复杂度为O(log n)。

 

 2.基本操作

测试:
 

import java.util.Map;
import java.util.Set;
import java.util.TreeMap;public class test {public static void main(String[] args) {Map<String,String> map=new TreeMap<>();map.put("齐天大圣","孙悟空");map.put("天蓬元帅","猪八戒");map.put("及时雨","宋江");//打印key对应valueSystem.out.println(map.values());//统计元素个数System.out.println(map.size());String s=map.get("及时雨");System.out.println(s);//删除元素,返回被删除的元素的valueString re=map.remove("及时雨");System.out.println(re);//将map中的key给到Set中Set<String> t=map.keySet();for (String str:t){System.out.print(str+" ");}System.out.println();//将map中的key和value给到Set中Set<Map.Entry<String,String>> entry=map.entrySet();System.out.println(entry);for (Map.Entry<String,String> e:entry){System.out.println(e.getKey()+"-->"+e.getValue());}//判断是否为空System.out.println(map.isEmpty());}
}

注意

  1. Map是一个接口,不能直接实现化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap
  2. Map中存放键值对的Key是唯一的,value值可以重复
  3. 在TreeMap中插入键值对时,Key不能为空,否则就会抛出空指针异常,value可以为空。但HashMap的key和value都可以为空。
  4. Map中的key可以全部分离出来,存储到Set中来进行访问(因为Key不重复)
  5. Map中的value可以全部分离出来,存储在Collextion的任何一个子类集合中(value可能有重复)
  6. Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行 重新插入

 总结:

  1. TreeSet是一种有序的、可排序的集合类。TreeSet底层使用红黑树数据结构实现,能够快速进行插入、删除和查找操作,且能保证元素的有序性。我们可以使用无参构造函数或带有Comparator参数的构造函数创建TreeSet对象,并使用相关方法进行添加、删除和查找操作。
  2. TreeMap是一种基于红黑树实现的有序映射表,它可以按照key的自然顺序或者自定义顺序进行排序,并具有查找和排序的功能,保证所有操作的时间复杂度为O(logn),但TreeMap的占用内存较大,插入和删除操作可能需要调整红黑树,会消耗较多的时间和资源。

以上就是本章所有内容,若有不足欢迎各位大佬指正~😄

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

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

相关文章

LLM大语言模型学习资料网站(git、gitee、等)

先上几个关于大模型方面比较好的开源地址吧&#xff1a; 1.清华智普ChatGLM&#xff1a; GitHub - THUDM/ChatGLM3: ChatGLM3 series: Open Bilingual Chat LLMs | 开源双语对话语言模型 ChatGLM3 series: Open Bilingual Chat LLMs | 开源双语对话语言模型&#xff0c;详细…

【计算机毕设】基于SpringBoot的医院管理系统设计与实现 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 本项目旨在设计并实现一个基于SpringBoot的医院管理系统&#xff0c;以提高医院管理效率&#xff0c;优化医疗服务流程&#xff0c;提升患者就诊体验…

Codeforces Round 949 (Div. 2)(A,B题解)

这场真是给我打的汗流浃背了&#xff0c;这场真的巨难&#xff08;可能是因为我二进制根本就没学好的原因吧&#xff09; 反正总共就搞了两道题&#xff0c;第一道题注重于思维&#xff0c;第二道题纯二进制&#xff0c;第三道题看着也是二进制&#xff08;最后时间不够了&…

Python知识点18---进程和线程

提前说一点&#xff1a;如果你是专注于Python开发&#xff0c;那么本系列知识点只是带你入个门再详细的开发点就要去看其他资料了&#xff0c;而如果你和作者一样只是操作其他技术的Python API那就足够了。 首先我要给大家说一下进程和线程有什么区别&#xff0c;进程包含着线…

详解 Spark SQL 核心编程知识

一、SparkSQL 概述 1. 概念 Spark SQL 是 Spark 用于结构化数据 (structured data) 处理的 Spark 模块&#xff0c;使用 SQL 的方式简化 RDD 的开发 2. Hive VS SparkSQL Hive 是早期唯一运行在 Hadoop 上的 SQL-on-Hadoop 工具&#xff0c;但是 MapReduce 计算过程中大量的中…

URL路由基础

本书1-7章样章及配套资源下载链接: https://pan.baidu.com/s/1OGmhHxEMf2ZdozkUnDkAkA?pwdnanc 源码、PPT课件、教学视频等&#xff0c;可以从前言给出的下载信息下载&#xff0c;大家可以评估一下。 对于高质量的Web应用来讲&#xff0c;使用简洁、优雅的URL设计模式非常…

时间序列的谱分解

refer&#xff1a;15.pdf (berkeley.edu) Stat 153 Fall 2010 (berkeley.edu)

linux mtd分区应用操作sample之某分区擦除

什么是擦除? 把flash相关的区域数据bit置为1的过程 #include <mtd/mtd-user.h> #include <mtd/mtd-abi.h> struct erase_info_user {__u32 start; // 起点 __u32 length; //长度 块大小对齐 不然报参数失败 };struct erase_info_user64 {__u64 sta…

Linux 驱动设备匹配过程

一、Linux 驱动-总线-设备模型 1、驱动分层 Linux内核需要兼容多个平台&#xff0c;不同平台的寄存器设计不同导致操作方法不同&#xff0c;故内核提出分层思想&#xff0c;抽象出与硬件无关的软件层作为核心层来管理下层驱动&#xff0c;各厂商根据自己的硬件编写驱动…

【逻辑回归】Logistic Regression逻辑回归模型学习笔记

文章目录 序言1. 线性回归2. 逻辑回归2.1 引入逻辑回归的原因2.2 逻辑回归2.3 逻辑回归的应用 3. 逻辑函数3.1 sigmoid函数3.2 sigmoid函数的性质3.3 决策边界3.4 对数几率 4. 损失函数4.1 为什么说逻辑回归时概率类模型4.2 为什么要进行极大似然估计4.3 利用MLE如何推导出损失…

Linux下的配置工具menuconfig+配置文件(Kconfig/.config/defconfig)

我们都知道,嵌入式开发中,或者说C语言中,配置基本都是通过宏定义来决定的,在MCU开发中,代码量比较小,配置项也比较少,我们直接修改对应的宏定义即可。 但是,Linux开发中,操作系统、驱动部分还有应用部分加起来,代码量极大,配置项目也非常多,这时候,就需要对这些配…

HackTheBox-Machines--Cronos

文章目录 0x01 信息收集0x02 命令注入漏洞0x03 权限提升 Cronos 测试过程 0x01 信息收集 1.端口扫描 发现 SSH&#xff08;22&#xff09;、DNS&#xff08;53&#xff09;、HTTP&#xff08;80&#xff09;端口 nmap -sC -sV 10.129.227.2112.53端口开启&#xff0c;进行DNS…

【LeetCode刷题】前缀和解决问题:742.寻找数组的中心下标、238.除自身以外数组的乘积

【LeetCode刷题】Day 15 题目1&#xff1a;742.寻找数组的中心下标思路分析&#xff1a;思路1&#xff1a;前缀和思想 题目2&#xff1a;238.除自身以外数组的乘积思路分析思路1&#xff1a;前缀和思想 题目1&#xff1a;742.寻找数组的中心下标 思路分析&#xff1a; 其实题干…

【软件开发规范篇】前言

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…

Linux——多线程(三)

在上一篇博客中我们讲到了在加锁过程中&#xff0c;线程竞争锁是自由竞争的&#xff0c;竞争能力强的线程会导致其他线程抢不到锁&#xff0c;访问不了临界资源导致其他线程一直阻塞&#xff0c;造成其它线程的饥饿问题&#xff0c;想要解决此问题又涉及一个新概念线程同步 一…

18 EEPROM读写

EEPROM 简介 EEPROM (Electrically Erasable Progammable Read Only Memory&#xff0c;E2PROM)即电可擦除可编程只读存储器&#xff0c;是一种常用的非易失性存储器&#xff08;掉电数据不丢失&#xff09;&#xff0c;EEPROM 有多种类型的产品&#xff0c;此次实验使用的是A…

32位与64位程序下函数调用的异同——计科学习中缺失的内容

前言 今天&#xff0c;通过一个有趣的案例&#xff0c;从反编译的角度看一下C语言中函数参数是如何传递的。 创建main.c文件&#xff0c;将下面实验代码拷贝到main.c文件中。 # main.c #include <stdio.h>int test(int a, int b, int c, int d, int e, int f, int g, …

Docker最新超详细版教程通俗易懂

文章目录 一、Docker 概述1. Docker 为什么出现2. Docker 的历史3. Docker 能做什么 二、Docker 安装1. Docker 的基本组成2. 安装 Docker3. 阿里云镜像加速4. 回顾 hello-world 流程5. 底层原理 三、Docker 的常用命令1. 帮助命令2. 镜像命令dokcer imagesdocker searchdocker…

解锁数据宝藏:高效查找算法揭秘

代码下载链接&#xff1a;https://gitee.com/flying-wolf-loves-learning/data-structure.git 目录 一、查找的原理 1.1 查找概念 1.2 查找方法 1.3平均查找长度 1.4顺序表的查找 1.5 顺序表的查找算法及分析 1.6 折半查找算法及分析 1.7 分块查找算法及分析 1.8 总结…

pytorch学习笔记5

transform 本质上作用是将图片通过transform这个这个工具箱获取想要的结果 tensor就是一个包含神经网络需要的一些理论基础的参数 from torch.utils.tensorboard import SummaryWriter from torchvision import transforms from PIL import Image #tensor数据类型 #通过tra…