【数据结构】搜索树 与 Java集合框架中的Set,Map

news2024/11/17 16:42:21

作者主页:paper jie_博客

本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。

本文录入于《JAVA数据结构》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将javaSE基础知识一网打尽,希望可以帮到读者们哦。

其他专栏:《算法详解》《C语言》《javaSE》等

内容分享:本期将会分享java数据结构中的二叉搜索树与Java集合中的Set和Map

目录

什么是搜索树

搜索树的模拟实现

查找功能

代码实现

画图分析

新增功能

具体代码

画图分析

删除功能

具体代码

画图分析

搜索树的性能

搜索树与Java类集合的关系

Set和Map

概念

模型

Map

Map.Entry,v>

Map的常用方法

TreeMap和HashMap的比较

Set

Set的常用方法

TreeSet和HashMap的比较


什么是搜索树

搜索树又名二叉搜索树,它是一颗完全二叉树,且:

左树不为空的话,则左子树上的所有节点的值都小于根节点的值

右树不为空的话,则右子树上的所有节点的值都大于根节点的值

它的左右子树也是搜索树

搜索树的模拟实现

查找功能

实现这个功能就可以利用它的性质,比根节点的小的数在左边,比根节点大的数在右边,通过遍历的方式一直查找,要是遇到了null,代表就没有这个数。

代码实现

//查找元素
    //查找复杂度:O(logN)
    //最坏情况:O(N)
    public boolean search(int node) {
        if(root == null) {
            return false;
        }
        TreeNode cur = root;
        while(cur != null) {
            if(node < cur.val) {
                cur = cur.left;
            }else if(node > cur.val) {
                cur = cur.right;
            }else {
                return true;
            }
        }
        return false;
    }

画图分析

新增功能

新增节点,我们就分两种情况,一种没有节点,一种有节点,但大致开始用cur遍历找到需要插入的位置,再用一个prev记住cur的前一个节点。且相同是不需要添加的。当cur等于null的时候,就用prev来判断它大于key,就将新增节点放在它的左边不然就放在右边

具体代码

//新增元素
    public boolean push(int node) {
        if(root == null) {
            root = new TreeNode(node);
            return true;
        }

        TreeNode cur = root;
        TreeNode prve = null;

        while(cur != null) {
            if(node < cur.val) {
                prve  = cur;
                cur = cur.left;
            }else if(node > cur.val){
                prve = cur;
                cur = cur.right;
            }else {
                return false;
            }
        }
        TreeNode treeNode = new TreeNode(node);
        if(prve.val > node) {
            prve.left = treeNode;
        }else {
            prve.right = treeNode;
        }
        return true;
    }

画图分析

删除功能

删除就比较复杂一点,得分几种情况,而这几种情况中又得细分:

当需要删除的节点左边没有元素

1 需要删除的是头节点

2 需要删除的在父节点的左边

3 需要删除的节点在父节点的右边

当需要删删出的节点右边没有元素

1 需要删除的是头节点

2 需要删除的在父节点的左边

3 需要删除的节点在父节点的右边

当需要删除的节点两边都有元素

这里是比较难处理的,我们不能直接删除这个结点,这里我们使用替换法:找到删除节点右边的最小节点,将最小节点的值赋值给需要删除的那个元素,再将最小节点删除,在删除这个最小节点的时候我们也要考虑:

它的左边有没有元素,它的右边有没有元素,还是左右都没有元素

具体代码

//删除元素
    public boolean poll(int key) {
        TreeNode cur = root;
        TreeNode parent = null;
        while(cur != null) {
            if(key < cur.val) {
                parent = cur;
                cur = cur.left;
            }else if(key > cur.val) {
                parent = cur;
                cur = cur.right;
            }else {
                removeNode(cur, parent);
                return true;
            }
        }
        return false;
    }
    private void removeNode(TreeNode cur, TreeNode parent) {
        //删除节点左边为空
        if(cur.left == null) {
            if(cur == root) {
                root = root.right;
            }else if(parent.left == cur) {
                parent.left = cur.right;
            }else {
                parent.right = cur.right;
            }
            //删除节点右边为空
        }else if(cur.right == null) {
            if(root == cur) {
                root = root.left;
            }else if(parent.left == cur) {
                parent.left = cur.left;
            }else {
                parent.right = cur.left;
            }
            //都不为空
        }else {
            TreeNode xparent = cur;
            TreeNode x = cur.right;
            while(x.left != null) {
                xparent = x;
                x = x.left;
            }
            cur.val = x.val;
            if(xparent.left == x) {
                xparent.left = x.right;
            }else {
                xparent.right = x.right;
            }
        }

    }
}

画图分析

搜索树的性能

这里我们可以把查找的效率看做整个搜索树的性能,因为不管是新增和删除都是需要查找的嘛。

对于搜索树,我们知道,它就是一颗二叉树,所以比较的次数就是树的深度。但是所有情况都是这样嘛,这里会因为一个数据集合,如果他们数据插入的次序不一样,则会得到不一样的数据结构,比如下图:

这里我们就知道了,在最坏情况下搜索树会退化成一个链表所以:

最好情况时间复杂度:O(logN)

最坏情况时间复杂度:O(N)

搜索树与Java类集合的关系

Java中的TreeMap和TreeSet就是利用搜索树实现的,但是呢它们底层使用的是搜索树的进化再进化版红黑树(搜索树 - LAV树 - 红黑树 ),LAV树就是对搜索树的改进,遇到链表情况下它就是翻转这个链表,让他变成一个高度平衡的搜索树,而红黑树是在这个基础加上颜色一节红黑树性质的验证进一步的提高了它的效率。

Set和Map

概念

Map和Set是一种专门用来进行搜索的容器或者数据结构,它的搜索效率和实现它们的子类有关。一般比较简单的搜索方式有:

直接遍历,复杂度为O(N),效率比较底下

二分查找,复杂度为O(logN),但它麻烦的是必须是有序的

而这些搜索方式只适合静态的搜索,但对于区间这种插入和删除的操作他们就不行了,这种就属于动态搜索方式。Map和Set就是用来针对这种的动态查找的集合容器

模型

这集合中,一般把搜索的数据称为关键字Key和关键字的对应值Value,这两个称为键值对,一般有两种模型:

纯Key模型,即只需要一个数据,比如:

查找手机里面有没有这个联系人

Key-Value模型,比如:

查找这个篇文章里面这个词出现了几次 (词,出现的次数)

Map就是Key-Value模型,Set是纯Key模型

Map接口下继承了HashMap和TreeMap两个类,而Set接口下继承了TreeSet和HashSet两个类

TreeMap和TreeSet底层是红黑树,而HashMap和HashSet底层是哈希表

Map

Map是一个接口,它没有和其他几个类一样继承Collection,它存储的是<K,V>键值对,且K是唯一·的,V可以重复

Map.Entry<K,V>

Map.Entry<K,V>在Map中是一个内存类, 它是用来Map内部实现存放<K,V>键值对映射关系的,它还提供了Key,value的设置和Key的比较方式:

方法解释
K getKey()返回Entry中的Key
K getValue()返回Entry中的Value
K setValue(V value)

设置Entry中的value

这里要注意它没有提供设置Key的方法

Map的常用方法

注意事项:

1 Map是一个接口,它不可以实例化对象,要实例化只能实例化它的子类TreeMap或者HashMap

2 Map中存放键值对里的Key是唯一的,value是可以重复的

3 在TreeMap中插入键值对时,Key不能为空,否则就会抛NullPointerExecption异常,value可以为空。但是HashMap的Key和value都可以为空

4 Map中的Key是可以分离出来的,存储到Set中来进行访问(因为Key不能重合)

5 Map中的value也可以分离出来,存放到Collection的任何一个子集合中,但是value可能会重合 

6 Map中的键值对Key不能直接修改,value可以修改,如果要修改Key,只能先将Key先删除,再来插入

TreeMap和HashMap的比较

Map底层结构TreeMapHashMap
底层结构红黑树哈希桶
时间复杂度O(logN)O(1)
是否有序有序无序
线程安全不安全不安全
插入/删除/添加的区别需要进行数据间的比较直接通过计算哈希函数来确定地址
应用场景在需要有序的场景下不关心是否有序,更需要效率的场景下
比较和重写Key必须是可以比较的,不可以为null自定义类型需要重写哈希Code方法和equals方法

使用栗子:

HashMap:

public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("猪八戒", 500);
        map.put("孙悟空", 550);
        map.put("唐僧", 40);
        map.put("沙和尚", 100);
        map.put("白龙马", 300);
        System.out.println(map.get("猪八戒"));
        System.out.println(map.remove("八戒"));
        System.out.println(map.get("猪八戒"));
        Set<Map.Entry<String, Integer>> set = map.entrySet();
        for(Map.Entry<String, Integer> entry : set) {
            System.out.println(entry);
        }
        System.out.println(map.containsKey("猪八戒"));

    }

TreeMap:

public static void TestMap() {
        Map<String, String> m = new TreeMap<>();
// put(key, value):插入key-value的键值对
// 如果key不存在,会将key-value的键值对插入到map中,返回null
        m.put("林冲", "豹子头");
        m.put("鲁智深", "花和尚");
        m.put("武松", "行者");
        m.put("宋江", "及时雨");
        String str = m.put("李逵", "黑旋风");
        System.out.println(m.size());
        System.out.println(m);
// put(key,value): 注意key不能为空,但是value可以为空
// key如果为空,会抛出空指针异常
// m.put(null, "花名");
        str = m.put("无名", null);
        System.out.println(m.size());
// put(key, value):
// 如果key存在,会使用value替换原来key所对应的value,返回旧value
        str = m.put("李逵", "铁牛");
// get(key): 返回key所对应的value
// 如果key存在,返回key所对应的value
// 如果key不存在,返回null
        System.out.println(m.get("鲁智深"));
        System.out.println(m.get("史进"));
//GetOrDefault(): 如果key存在,返回与key所对应的value,如果key不存在,返回一个默认值
        System.out.println(m.getOrDefault("李逵", "铁牛"));
        System.out.println(m.getOrDefault("史进", "九纹龙"));
        System.out.println(m.size());
//containKey(key):检测key是否包含在Map中,时间复杂度:O(logN)
// 按照红黑树的性质来进行查找
// 找到返回true,否则返回false
        System.out.println(m.containsKey("林冲"));
        System.out.println(m.containsKey("史进"));
// containValue(value): 检测value是否包含在Map中,时间复杂度: O(N)
// 找到返回true,否则返回false
        System.out.println(m.containsValue("豹子头"));
        System.out.println(m.containsValue("九纹龙"));
// 打印所有的key
// keySet是将map中的key防止在Set中返回的
        for (String s : m.keySet()) {
            System.out.print(s + " ");
        }
        System.out.println();
// 打印所有的value
// values()是将map中的value放在collect的一个集合中返回的
        for (String s : m.values()) {
            System.out.print(s + " ");
        }
        System.out.println();
// 打印所有的键值对
// entrySet(): 将Map中的键值对放在Set中返回了
        for (Map.Entry<String, String> entry : m.entrySet()) {
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }
        System.out.println();
    }

Set

Set和Map的不同点就在于Set是继承于Collection接口类的,Set只存储了Key。

Set的常用方法

注意事项:

1 Set是继承Collection的一个接口

2 Set只存储Key,且要求Key是唯一的

3 TreeSet的底层是使用了Map来实现的,其使用Key与Object的一个默认对象作为键值对插入到Map中

4 Set的最大特点就是去重

5 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedSet,它是在HashSet上维护了一个双向链表来记入插入元素的次序

6 Set中的Key不能修改,如果要修改的话,呀先将原来的删除,再重新插入

7 TreeSeet中不能插入null的Key,但HashSet可以

TreeSet和HashMap的比较

Set底层结构TreeSet

HashSet

底层结构红黑树哈希桶
复杂度O(logN)O(1)
是否有序有序无序
线程安全不安全不安全
插入/删除/查找区别使用红黑树的性质来插入和删除的使用哈希函数来计算插入和删除的地址
比较和重写Key必须可以比较,不能为null自定义类型需要重写equals方法和HashCode方法
应用场景需要Key有序场景下需要更高的时间性能

栗子:

public static void TestSet2(){
        Set<String> s = new TreeSet<>();
// add(key): 如果key不存在,则插入,返回ture
// 如果key存在,返回false
        boolean isIn = s.add("apple");
        s.add("orange");
        s.add("peach");
        s.add("banana");
        System.out.println(s.size());
        System.out.println(s);
        isIn = s.add("apple");
// add(key): key如果是空,抛出空指针异常
//s.add(null);
// contains(key): 如果key存在,返回true,否则返回false
        System.out.println(s.contains("apple"));
        System.out.println(s.contains("watermelen"));
// remove(key): key存在,删除成功返回true
// key不存在,删除失败返回false
// key为空,抛出空指针异常
        s.remove("apple");
        System.out.println(s);
        s.remove("watermelen");
        System.out.println(s);
        Iterator<String> it = s.iterator();
        while(it.hasNext()){
            System.out.print(it.next() + " ");
        } 
        System.out.println();
    }

这里文章中我们提到的HashMap和HashSet的底层 - 哈希桶,下一篇文章就会介绍,期待一下吧

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

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

相关文章

C# OpenCvSharp Yolov8 Face Landmarks 人脸特征检测

效果 项目 代码 using OpenCvSharp; using OpenCvSharp.Dnn; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;namespace OpenCvSharp_Yolov8_Demo {public partial class frmMain…

C++数据结构X篇_25_堆排序(不稳定的排序)

本篇根据十大经典排序算法-堆排序算法详解进行整理和补充。 文章目录 1. 基础知识点1.1 完全二叉树1.2 堆的基础知识 2. 堆排序2.1 什么是堆排序2.2 算法原理2.2.1 理解方法12.2.2 理解方法2 2.3 算法实现 3. 堆排序算法特点3.1 时间复杂度3.2 空间复杂度3.3 稳定性 1. 基础知…

深度学习| U-Net网络

U-Net网络 基础知识和CNN的关系反卷积ReLU激活函数 U-Net入门U-Net网络结构图为什么需要跳跃连接U-Net的输入U-Net的应用 基础知识 理解U-Net网络结构需要相关知识点。 和CNN的关系 U-Net也是CNN&#xff08;Convolutional Neural Network&#xff0c;卷积神经网络&#xff…

网络架构学习1

文章目录 网络架构学习11. 传统CNN卷积神经网络1.1 基本思想1.2 VCG16(经典CNN网络架构之一) 2. 两种经典的网络架构2.1 FCN网络2.2 U-Net网络 3. FCNVMB(基于U-Net架构)3.1 FCNVMB 主要思想3.2 FCNVMB 提供的其他思想 网络架构学习1 1. 传统CNN卷积神经网络 1.1 基本思想 C…

Android SurfaceFlinger做Layer合成时,如何与HAL层进行交互

目录 零、本文讨论问题的范围一、问题&#xff1a;SurfaceFlinger图层合成选择实现方式的两难1.1 从OpenGL ES、HWC本身来讲1.2 以HWC为主导的判断逻辑 二、SurfaceFlinger与HAL层进行交互的具体实现框架2.1 SurfaceFlinger 调用 OpenGL ES 流程2.2 FrameBuffer2.3 SurfaceFlin…

c语言从入门到实战——数组

数组 前言1. 数组的概念2. 一维数组的创建和初始化2.1 数组创建2.2 数组的初始化2.3 数组的类型 3. 一维数组的使用3.1 数组下标3.2 数组元素的打印3.3 数组的输入 4. 一维数组在内存中的存储5. sizeof计算数组元素个数6. 二维数组的创建6.1 二维数组得概念6.2 二维数组的创建 …

Java集成腾讯云OCR身份证识别接口

一、背景 项目用到身份证识别获取人员信息的功能&#xff0c;于是想到了腾讯云提供这样的API。在整合代码过程都很顺利&#xff0c;利用腾讯云官方SDK很快集成进来。但是在上测试环境部署时有了新的问题&#xff0c;通过Nginx代理后的环境无法访问到目标腾讯云接口&#xff0c;…

buuctf_练[CSCCTF 2019 Qual]FlaskLight

[CSCCTF 2019 Qual]FlaskLight 文章目录 [CSCCTF 2019 Qual]FlaskLight掌握知识解题思路关键paylaod 掌握知识 内置函数的过滤&#xff0c;globals变量的过滤&#xff0c;调用内部变量或函数的OS函数进行命令执行 解题思路 打开题目链接&#xff0c;很明显看标题和内容是fla…

【动态基础】从暴力递归到动态规划

C面经汇总 系列综述&#xff1a; 目的&#xff1a;本系列是个人整理为了秋招和实习面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡背诵量与深入程度。 来源&#xff1a;材料主要源于算法大神&#xff08;左程云&#xff09;教你从暴力递归到动态规划进行的&#xf…

vue实现连接线

效果展示 实现代码 下载插件npm install --save leader-line-vue <template><div class"wrap"><div ref"start" class"start">start</div><div ref"end" class"end">end</div></d…

数据结构时间复杂度(补充)和空间复杂度

Hello&#xff0c;今天事10月27日&#xff0c;距离刚开始写博客已经过去挺久了&#xff0c;我也不知道是什么让我坚持这么久&#xff0c;但是学校的课真的很多&#xff0c;很少有时间多出来再学习&#xff0c;有些科目马上要考试了&#xff0c;我还不知道我呢不能过哈哈哈&…

Django 全局配置 settings 详解

文章目录 1 概述1.1 Django 目录结构 2 常用配置&#xff1a;settings.py2.1 注册 APP&#xff1a;INSTALLED_APPS2.2 模板路径&#xff1a;TEMPLATES2.3 静态文件&#xff1a;STATICFILES_DIRS2.4 数据库&#xff1a;DATABASES2.5 允许访问的主机&#xff1a;ALLOWED_HOSTS 1 …

[SQL开发笔记]UPDATE 语句:更新表中的记录

一、功能描述&#xff1a; UPDATE 语句&#xff1a;用于更新表中的记录 二、UPDATE 语句语法详解&#xff1a; UPDATE 语法 UPDATE table_nameSET column1value1,column2value2,...WHERE some_columnsome_value; 参数说明&#xff1a; 1.table_name&#xff1a;要修改的表…

【Docker】github Actions自动构建

通过github的Actions 实现代码push仓库后自动构建容器并发布到DockerHub. 创建项目 首先我们创建一个项目,这里我就用Vue项目进行演示. npm init vuelatest Actions-demo-swback进去项目&#xff0c;按照提示执行 npm install npm run dev 启动项目. 首先保证项目的正常启动…

DAY36 738.单调递增的数字 + 968.监控二叉树

738.单调递增的数字 题目要求&#xff1a;给定一个非负整数 N&#xff0c;找出小于或等于 N 的最大的整数&#xff0c;同时这个整数需要满足其各个位数上的数字是单调递增。 &#xff08;当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单…

Python机器学习基础(一)---数据集加载的方法

几个数据集加载的方式 鸢尾花练习资源(这个资源有瑕疵&#xff0c;index列和Species 都是带”“的字符串 导致一些加载现实问题&#xff0c;从而验证 还是pandas最好用) "index","Sepal.Length","Sepal.Width","Petal.Length","…

echarts中横向柱状图的数字在条纹上方

实现效果&#xff1a; 数字在条纹的上方 实现方法&#xff1a;这些数字是用新添加一个坐标轴来实现的 直接添加坐标轴数字显示是在条纹的正右边 所以需要配置一下偏移 完整代码 var option {grid: {left: "3%",right: "4%",bottom: "3%",cont…

FreeROTS 任务通知和实操 详解

目录 什么是任务通知&#xff1f; 任务通知值的更新方式 任务通知的优势和劣势 任务通知的优势 任务通知的劣势 任务通知相关 API 函数 1. 发送通知 2. 等待通知 任务通知实操 1. 模拟二值信号量 2. 模拟计数型信号量 3. 模拟事件标志组 4. 模拟消息邮箱 什么是任务…

高防CDN:网络攻防的坚强防线

在当今数字化时代&#xff0c;网络攻击已经成为一种常态&#xff0c;对企业和个人的网络资产构成了严重威胁。为了应对这些风险&#xff0c;高防CDN&#xff08;Content Delivery Network&#xff09;已经崭露头角&#xff0c;它不仅提供内容分发&#xff0c;还整合了强大的网络…

电脑上使用的备忘记事软件哪一款好用点?

生活中充斥着大大小小的任务&#xff0c;如工作方面、学习方面、生活方面等&#xff0c;多种事务掺杂交错在一起非常容易忘记&#xff0c;为避免忘记重要的事情&#xff0c;大家可以借助电脑上好用的备忘录工具来进行记事。 支持在电脑上使用的备忘录软件是比较多的&#xff0…