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

作者主页: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.mfbz.cn/a/107653.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TSINGSEE青犀睡岗离岗检测算法——确保加油站安全运营

众所周知&#xff0c;加油站是一个需要24小时营业的场所&#xff0c;由于夜间加油人员较少&#xff0c;员工极易处于疲劳或者睡眠状态&#xff0c;为保障安全和效率&#xff0c;通过TSINGSEE青犀睡岗离岗检测算法在加油站场景中&#xff0c;可以及时发现工作人员的疲劳状况&…

JAVA面试题简单整理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、重载和重写的区别一、&和&&的区别一、get和post请求的区别 delete、put一、cookie和session的区别一、Autowired和Resource区别一、”和equals…

笔记:电子设备接地,接的到底是什么地?

电路中有“地”&#xff0c;设备中有“地”&#xff1b;都是“地”&#xff0c;此地非彼地。 混淆的原因 有些混淆&#xff0c;是以为中文翻译造成的&#xff0c;英文所有Ground都统一翻译为“地”&#xff1b; 例1&#xff1a;英文Circuit Ground&#xff0c;应该翻译为电路…

mac安装并使用wireshark

mac安装并使用wireshark 1 介绍 我们在日常开发过程中&#xff0c;遇到了棘手的问题时&#xff0c;免不了查看具体网络请求情况&#xff0c;这个时候就需要用到抓包工具。比较著名的抓包工具就属&#xff1a;wireshark、fildder。我这里主要介绍wireshark。 2 安装 以mac安装为…

Android-宝宝相册(第四次作业)

第四次作业-宝宝相册 题目 用Listview建立宝宝相册&#xff0c;相册内容及图片可自行设定&#xff0c;也可在资料文件中获取。给出模拟器仿真界面及代码截图。 &#xff08;参考例4-8&#xff09; 创建工程项目 创建名为baby的项目工程&#xff0c;最后的工程目录结构如下图所…

自学爬虫—作业1—requests模块

视频&#xff1a; 要求&#xff1a; 肯德基地址查询&#xff0c;爬某个关键字&#xff0c;获取下面的所有page的信息&#xff0c;存到一个json或者txt。 代码&#xff1a; 关键点&#xff0c;&#xff08;1&#xff09;每一个ajax的请求第一个键值对就是所有获得的地址的总数…

【算法|动态规划No.31 | 01背包问题】01背包模板题

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

【Linux】安装与配置虚拟机及虚拟机服务器坏境配置与连接---超详细教学

一&#xff0c;操作系统介绍 1.1.什么是操作系统 操作系统&#xff08;Operating System&#xff0c;简称OS&#xff09;是一种系统软件&#xff0c;它是计算机硬件和应用软件之间的桥梁。它管理计算机的硬件和软件资源&#xff0c;为应用程序提供接口和服务&#xff0c;并协…

如何使用ffmpeg制作透明背景的视频

最近我们尝试在网页上叠加数字人讲解的功能&#xff0c;发现如果直接在网页上放一个矩形的数字人视频&#xff0c;效果会很差&#xff0c;首先是会遮挡很多画面的内容&#xff0c;其次就是不管使用任何任务背景&#xff0c;画面都和后面的网页不是很协调&#xff0c;如图所示&a…

【操作系统】文件管理大题总结

【操作系统】文件管理大题总结 文章目录 【操作系统】文件管理大题总结前置知识操作系统中的存储单位转换 1、目录管理中的典型问题分析基础例题&#xff1a;往年真题 2、外存的组织方式中的典型问题分析基础例题王道课后题往年真题 3、文件存储空问管理中的典型问题分析基础例…

JS问题:如何实现文本一键复制和长按复制功能?

前端功能问题系列文章&#xff0c;点击上方合集↑ 序言 大家好&#xff0c;我是大澈&#xff01; 本文约2000字&#xff0c;整篇阅读大约需要4分钟。 本文主要内容分三部分&#xff0c;第一部分是需求分析&#xff0c;第二部分是实现步骤&#xff0c;第三部分是问题详解。 …

抓取网页的含义和URL基本构成

抓取网页是指通过爬虫程序从互联网上获取网页的内容和数据。抓取网页是爬虫的核心功能之一&#xff0c;通过抓取网页&#xff0c;可以获取到网页中的文本、图片、链接等信息&#xff0c;用于后续的数据分析、挖掘和应用。 URL&#xff08;Uniform Resource Locator&#xff09…

PHP递归实现无限级分类

什么是无限级分类&#xff1f; 无限级分类是一种对商品或信息进行分类的方式&#xff0c;在这种分类方式中&#xff0c;每个分类都可以再次细分出更多的子分类&#xff0c;形成无限的级别 应用场景&#xff1a; 一个电商网站的分类可以是&#xff1a;服装、鞋类、家居用品等…

保姆级教学安装Linux操作系统,以及Linux的语法入门

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Linux》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;这个专…

人工智能基础_机器学习003_有监督机器学习_sklearn中线性方程和正规方程的计算_使用sklearn解算八元一次方程---人工智能工作笔记0042

然后我们再来看看,如何使用sklearn,来进行正规方程的运算,当然这里 首先要安装sklearn,这里如何安装sklearn就不说了,自己查一下 首先我们还是来计算前面的八元一次方程的解,但是这次我们不用np.linalg.solve这个 解线性方程的方式,也不用 直接 解正规方程的方式: 也就是上面…

基于Kubesphere容器云平台物联网云平台Devops实践

基于Kubesphere容器云平台物联网云平台Devops实践 项目背景 ​ 公司是做工业物联网相关业务的&#xff0c;现业务是云平台&#xff0c;技术栈 后端为 Springboot2.7JDK11 &#xff0c;前端为 Vue3Ts&#xff0c;需要搭建自动化运维平台以实现业务代码自动部署上线&#xff0c;…

【NLP】word复制指定内容到新的word文档

目录 1.python代码 2.结果 需求&#xff1a; 复制word文档里的两个关键字&#xff08;例如“起始位置”到“结束位置”&#xff09;之间的内容到新的word文档。 前提&#xff1a;安装win32包&#xff0c;通过pip install pywin32命令直接安装。话不多说&#xff0c;直接上代码…

Mac 安装nvm

安装方案&#xff1a; 1. 从github下载nvm仓库到 ~/目录 地址&#xff1a;https://github.com/nvm-sh/nvm.git git clone https://github.com/nvm-sh/nvm.git 2. 进入nvm目录中执行install.sh等待执行完成&#xff0c;执行的操作方法就是直接将文件拖入到终端然后回车。 3.…

Linux下自动挂载U盘或者USB移动硬盘

最近在折腾用树莓派&#xff08;实际上是平替香橙派orangepi zero3&#xff09;搭建共享文件服务器&#xff0c;有一个问题很重要&#xff0c;如何在系统启动时自动挂载USB移动硬盘。 1 使用/etc/fstab 最开始尝试了用/etc/fstab文件下增加:"/dev/sda1 /home/orangepi/s…

阿里云企业邮箱基于Spring Boot快速实现发送邮件功能

邮件在项目中经常会被用到&#xff0c;比如用邮件发送通知。比如&#xff0c;通过邮件注册、认证、找回密码、系统报警通知、报表信息等。本篇文章带大家通过SpringBoot快速实现一个发送邮件的功能。 邮件协议 下面先简单了解一下常见的邮件协议。常用的电子邮件协议有SMTP、…
最新文章