数据结构——二叉搜索树、Map和Set

news2024/9/29 5:45:01

对于不同的数据结构,他们的使用场景是不一样的,map和set这两种数据结构主要用在搜索相关的场景中。学习这些之前我们先来了解一下二叉搜索树,

一、搜索树

1.1概念

二叉搜索树 又称 二叉排序树 ,它或者是一棵空树,或者是具有以下性质的二叉树 :
<1>左子树上所有的值都小于根节点的值;
<2>右子树上所有的值都大于根结点的值;
<3>子树也是一颗二叉搜索树;


1.2 搜索树操作

1.2.1树的查找操作

代码如下:

 public TreeNode search(int val) {
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val < val) {
                cur = cur.right;
            } else if (cur.val > val) {
                cur = cur.left;
            } else {
                return cur;
            }
        }
        return null;
    }


1.2.2树的插入操作

与树的查找操作一样,也需要与树的节点进行大小比较,cur的移动方法与查找相同,当我们还需要定义一个parent记录cur的父亲节点,当cur为空时,要插入的值就可以通过与parent节点的值比较大小来确定插入到parent的左节点还是右节点

public void insert(int val) {
        if (root == null) {
            root = new TreeNode(val);
            return;
        }
        TreeNode cur = root;
        //记录cur的父亲节点
        TreeNode parent = null;
        while (cur != null) {
            parent=cur;
            if (val > cur.val) {
                cur = cur.right;
            } else if(val<cur.val){
                cur = cur.left;
            }else{
                //二叉搜索树不能有两个一样的值
                return;
            }
        }
        if(val>parent.val){
            parent.right=new TreeNode(val);
        }else{
            parent.left=new TreeNode(val);
        }
    }

1.2.3树的删除操作

实际上,在情况一和情况三中还分别有三种小情况:

代码如下:

private void removeNode(TreeNode parent, TreeNode cur) {
        //情况1:cur的左边为空
        if(cur.left==null){
            if(cur==root){
                //cur是根结点
                root=cur.right;
            }else if(parent.right==cur){
                //parent的右节点是cur
                parent.right=cur.right;
            }else if(parent.left==cur){
                //parent的左节点是cur
                parent.left=cur.right;
            }
        }else if(cur.right==null){//情况2:cur的右边为空
            if(cur==root){
                //cur是根结点
                root=cur.left;
            }else if(parent.right==cur){
                //parent的右节点是cur
                parent.right=cur.left;
            }else{
                //parent的左节点是cur
                parent.left=cur.left;
            }
        }else{//情况3:cur的左右都不为空
            TreeNode rightMin = cur.right;
            TreeNode parent2=cur;//记录rightMin的父亲节点
            while(rightMin.left!=null){
                parent2=rightMin;
                rightMin=rightMin.left;
            }
            //找到右树最小值,将cur.val覆盖,相当于删除了这个节点
            cur.val=rightMin.val;

            //删除rightMin节点,与情况一方法相同
             if(parent2.left==rightMin) {
                 parent2.left = rightMin.right;
             }else{
                 parent2.right=rightMin.right;
             }
        }
    }

1.2.4复杂度分析
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为: O( logN)
最差情况下,二叉搜索树退化为单支树,其平均比较次数为: O(N)
(当插入的数据有序时,二叉搜索树退化成一颗单分支的树,时间复杂度最大)
1.2.5  java 类集的关系
TreeMap TreeSet java 中利用搜索树实现的 Map Set ;实际上用的是红黑树,而红黑树是一棵近似平衡的二叉搜索树,即在二叉搜索树的基础之上 + 颜色以及红黑树性质验证,关于红黑树的内容后序再进行讲解。

二、搜索

2.1概念与场景

Map set 是一种 专门用来进行搜索的容器或者数据结构 ,其搜索的效率与其具体的实例化子类有关 。以前常见的
搜索方式有:
1. 直接遍历 ,时间复杂度为 O(N) ,元素如果比较多效率会非常慢
2. 二分查找 ,时间复杂度为O(logN),但搜索前必须要求序列是 有序
上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如:
1. 根据姓名查询考试成绩
2. 通讯录,即根据姓名查询联系方式
3. 不重复集合,即需要先搜索关键字是否已经在集合中
可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,本节介绍的 Map和Set是一种适合动态查找的集合容器

2.2模型

2.2 模型
一般把搜索的数据称为关键字( Key ),和关键字对应的称为值( Value ),将其称之为 Key-value 的键值对,所以模型会有两种:
1. 纯 key 模型 ,比如:
    <1>有一个英文词典,快速查找一个单词是否在词典中
    <2>快速查找某个名字在不在通讯录中
2. Key-Value 模型 ,比如:
    <1>统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数: < 单词,单词出现的次数 >
    <2>梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号
Map中存储的就是key-value的键值对,Set中只存储了Key

三、Map的使用

Map 是一个接口类,该类没有继承自 Collection ,该类中存储的是 <K,V> 结构的键值对,并且 K 一定是唯一的,不可以重复。

3.1Map.Entry<k,v>

Map.Entry<K, V> Map 内部实现的用来存放 <key, value> 键值对映射关系的内部类 ,该内部类中主要提供了<key, value>的获取, value 的设置以及 Key 的比较方式。

!!!Map.Entry<k,v>中没有设置key的方法。 


3.1Map的常用方法

注意:
1. Map 是一个 接口 ,不能直接实例化对象 ,如果 要实例化对象只能 实例化其实现类TreeMap或者HashMap
2. Map 中存放键值对的 Key是唯一的,value是可以重复
3. TreeMap中插入键值对时,key不能为空 ,否则就会抛 NullPointerException 异常 value 可以为空。但是 HashMap的key和value都可以为空
4. Map 中的 Key 可以全部分离出来,存储到 Set 来进行访问 ( 因为 Key 不能重复 )
5. Map 中的 value 可以全部分离出来,存储在 Collection 的任何一个子集合中 (value 可能有重复 )
6. Map 中键值对的 Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行重新插入
7. TreeMap HashMap 的区别(HashMap后面会说到)
!!! entrySet方法中返回的映射关系相当于搜索树的每一个节点

四、Set的使用

4.1Set的常用方法

!!!Map不能使用迭代器,只有Set才可以,原因是Map没有实现Iterable接口,但是我们可以使用entrySet方法将Map的键值对放入Set中,在通过Set使用迭代器间接遍历Map

 public static void main(String[] args) {
       TreeSet<String> treeSet = new TreeSet<>();
        treeSet.add("abcd");
        treeSet.add("hello");
        treeSet.add("def");

        //判断元素是否存在于集合中,存在返回true
        System.out.println(treeSet.contains("hello"));

        //迭代器遍历
        Iterator<String> it = treeSet.iterator();
        while(it.hasNext()){
            System.out.println(it.next()+" ");
        }

    }

注意:
1. Set 是继承自 Collection 的一个接口类
2. Set 中只存储了 key ,并且要求 key 一定要唯一
3. TreeSet的底层是使用Map来实现的 其使用key与Object的一个默认对象作为键值对插入到Map中
4. Set 最大的功能 就是对集合中的元素进行 去重
5. 实现 Set 接口的常用类有 TreeSet HashSet ,还有一个 LinkedHashSet LinkedHashSet 是在 HashSet 的基础上 维护了一个双向链表来记录元素的插入次序
6. Set 中的 Key不能修改 ,如果要修改,先将原来的删除掉,然后再重新插入
7. TreeSet中不能插入null的key,HashSet可以
8. TreeSet HashSet 的区别【 最后会讲到】


五、哈希表

在TreeSet和TreeMap中查找值时,需要通过对关键码的多次比较,而红黑树的高度为logN,因此它们查询的时间复杂度为O(logN),

如果构造一种存储结构, 通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系 ,那么在查找时通过该函数可以很快 找到该元素。
当向该结构中:
插入元素
根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
搜索元素
对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功
该方式即为哈希 ( 散列 ) 方法, 哈希方法中使用的转换函数称为哈希 ( 散列 ) 函数,构造出来的结构称为哈希表 (Hash  Table)( 或者称散列表 )

5.1哈希冲突


5.2冲突-避免-负载因子调节  

由于填入表中的元素个数是无法改变的,因此我们只能通过增加散列表的长度来降低负载因子。 


5.3冲突-解决-开散列/哈希桶 

开散列法又叫链地址法 ( 开链法 ) ,首先对 关键码集合用散列函数计算散列地址 ,具有 相同地址的关键码归于同一子集合 ,每一个子集合称为一个桶, 各个桶中的元素通过一个单链表链接起来 ,各链表的 头结点存储在哈希表中

5.4HashMap代码实现 

class HashBuck {
    //桶中的节点
    static class Node {
        public int key;
        public int val;
        public Node next;

        public Node(int key, int val) {
            this.key = key;
            this.val = val;
        }
    }

    //负载因子大小
    public static final double DEFAULT_LOADFACTER = 0.75f;

    //数组
    public Node[] array = new Node[10];
    public int usedSize;

    //存放值
    public void push(int key,int val){
        int index = key % 10;//随便举例的一个哈希函数

        //首先要遍历对应链表,看是否已经存在key
        Node cur = array[index];
        while(cur!=null){
            if(cur.key==key){
                cur.val=val;
            }
            cur=cur.next;
        }

        //说明没有key节点
        Node node = new Node(key,val);
        node.next=array[index];
        array[index]=node;
        usedSize++;

        //计算负载因子大小
        if(doloadFactor()>=DEFAULT_LOADFACTER){
            //扩容
            reSize();
        }
    }

    private void reSize() {
        //注意不能直接拷贝到新数组,因为扩容后哈希函数已经发生了改变
        //需要重新遍历桶中的所有节点,重新把这些节点放到合适的桶中
        Node[] newArray = new Node[array.length*2];
        for (int i = 0; i < array.length; i++) {
            Node cur=array[i];

            while(cur!=null){
                int newIndex= cur.key% newArray.length;
                Node curN=cur.next;
                cur.next=newArray[newIndex];
                newArray[newIndex]=cur;
                cur=curN;
            }
        }
        //将array指向newArray即可
        array=newArray;
    }

    private double doloadFactor() {
        return usedSize*1.0/array.length;
    }


    public int getVal(int key){
        int index = key % 10;
        Node cur = array[index];
        while(cur!=null){
            if(cur.key==key){
               return cur.val;
            }
            cur=cur.next;
        }
        //没找到
        return -1;
    }

}

 

在对散列表进行reSize过程中,可能会遇到下面的问题:

 


5.5性能分析

  实际使用过程中,我们认为哈希表的冲突率是不高的,冲突个数是可控的,也就是每个桶中的链表的长度是一个 常数 ,所以,通常意义下,我们认为 哈希表的插入/删除/查找时间复杂度是O(1)

5.6哈希表和Java类集的关系

1. HashMap HashSet java 利用哈希表实现 Map Set
2. java 中使用的是 哈希桶 方式解决冲突的
3. java 会在冲突链表长度大于一定阈值后,将链表 转变为搜索树 (红黑树)
4. java 中计算哈希值实际上是调用的类的 hashCode 方法,进行 key 的相等性比较是调用 key equals 方法。所以如果要用自定义类作为 HashMap key 或者 HashSet 的值 必须覆写 hashCode 和 equals 方法 ,而且要做到 equals 相等的对象, hashCode 一定是一致的。

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

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

相关文章

宝塔面板,awk读取网站日志,筛选请求耗时排名前100的记录

背景 近期发现项目的访问量稳步上升 渐渐注意到一些接口响应时间过长&#xff0c;在此需设计读取接口耗时较长的日志&#xff08;nginx 的 access.log日志&#xff09; 在此将实现步骤记录如下&#xff0c;方便道友参考 . 环境配置 宝塔面板 Nginx 1.20.2 PHP-7.2.33实现步…

认知战认知作战:认知战目标对手分析,你需要知道的目标对手分析SOP

认知战认知作战&#xff1a;认知战目标对手分析&#xff0c;你需要知道的目标对手分析SOP 认知战认知作战&#xff1a;认知战目标对手分析你需要知道的目标对手分析SOP 关键词&#xff1a;认知战, 目标对手分析, 数据情报搜集, 自我审视, 洞悉对手, 精准攻击策略, 行动规划, …

【国家博物馆应对黄牛办法解析】

一 国家博物馆预约流程及独家预约问题 微信公众号的预约引导页&#xff0c;有微信小程序和PC端预约两种方式&#xff1a; PC端预约和微信小程序明明是两中方式&#xff0c;现在却变成一种了&#xff0c; 为何不能在支付宝小程序预约&#xff1f; 独家的背后往往有故事&#x…

从零开学C++:二叉搜索树

引言&#xff1a;在本篇博客当中&#xff0c;我们会将关于二叉树的进阶结构——二叉搜索树&#xff0c;强大的搜索效率让它在数据结构当中变得十分重要&#xff0c;让我们一起来进行学习吧&#xff01; 更多有关C的知识详解可前往个人主页&#xff1a;计信猫 一&#xff0c;二叉…

Android开发版本更新和强制更新效果

Android开发版本更新和强制更新效果 版本更新基本每个app都有&#xff0c;这里加多个强制更新效果 一、思路&#xff1a; 用okhttp下载接口返回的apk下载地址&#xff0c;这样有进度条 二、效果图&#xff1a; Android开发教程实战案例源码分享-版本更新和强制更新效果 三、…

建筑师设计师必备的14个AI工具

从制作复杂的设计到可视化突破性的建筑创新&#xff0c;建筑领域一直与技术紧密交织。随着我们深入数字时代&#xff0c;建筑师和设计师利用尖端人工智能 (AI) 工具的力量来革新他们的工艺也就不足为奇了。但是&#xff0c;随着市场上 AI 工具的大量出现&#xff0c;哪些工具真…

Pymsql 插入数据时报错 Unkown column xxxx in field list

如图 从文字来看&#xff0c;它是说我们表里没有“247dxxxxxxx”这个字段&#xff0c;但问题是我建表的时候本来就没建这个字段&#xff0c;这一串字符是我要插入到“id”这个字段里的内容&#xff0c;它怎么就把它当成字段了呢&#xff1f; 后来发现是在传入参数的时候&#…

Java 发布jar包到maven中央仓库(2024年9月保姆级教程)

文章目录 前言一、账号准备1. 注册登录账号2. 新建命名空间3. 验证命名空间4. 生成令牌5. 为 maven 设置令牌二、GPG准备1. 下载GPG2. 发布证书2.1 新建证书2.2 发布证书到服务器2.3 验证发布三、发布jar包到中央仓库1. 编辑项目pom文件2. 打包上传3. 发布jar包4. 搜索我们的ja…

分片上传和断点续传

今天介绍两个概念&#xff1a; 1、分片上传 分片上传&#xff0c;就是将所要上传的文件&#xff0c;按照一定的大小&#xff0c;将整个文件分隔成多个数据块来进行分别上传&#xff0c;上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。 2、断点续传 断点续…

C语言课程设计题目三:图书管理系统设计

题目三&#xff1a;图书管理系统设计 图书管理信息包括&#xff1a;图书名称、图书编号、单价、作者、存在状态、借书人姓名、性别、学号等。 主要功能&#xff1a; 新进图书基本信息的输入。图书基本信息的查询。对撤销图书信息的删除。为借书人办理注册。办理借书手续。办理…

数据流处理技术与Flink框架

一数据流 数据流定义&#xff1a; 数据流&#xff08;Data Stream&#xff09;是指数据以连续不断的方式到达和处理的序列。在现实世界中&#xff0c;许多数据来源都是以流的形式存在&#xff0c;比如&#xff1a; 1. 用户行为&#xff1a;用户在网站上的点击流、移动应用中…

微软 Win11 RP 226x1.4247(KB5043145)预览版发布!

系统之家于9月25日发出最新报道&#xff0c;微软面向Release Preview频道的用户发布了Win11的KB5043145更新。系统更新后版本号提升为22621.4247和22631.4247。此次更新新增多项功能&#xff0c;例如用户播放媒体时&#xff0c;媒体控件会显示在锁屏中央的下部。以下跟随小编看…

LeetCode2207解题思路

题目描述 字符串中最多数目的子序列 解题思路&#xff1a; 题目要求我们找到在 text 中 找到最多可组成 pattern 的字符串个数&#xff0c;并且允许在 text 的任意位置插入 pattern 中一个字符&#xff0c;也就是说我们只需要考虑 text 中的 pattern 含有的字符即可。例如示例…

【React】组件基础使用

1. react组件 在react中&#xff0c;组件就是首字母大写的函数&#xff0c;内部存放了组件的逻辑、UI&#xff0c;渲染组件只需要把组件当成标签书写。 使用组件有两种方式&#xff1a;自闭和 、成对标签 function App() {// 定义组件function Component() {return <div&…

从入门到入土:计算机视觉CV学习路线图

在当今这个被数据和图像淹没的世界&#xff0c;计算机视觉&#xff08;CV&#xff09;正如一位聪明绝顶的魔术师&#xff0c;能够从无数的图像中提取出有意义的信息。对于那些初入这个领域的新人&#xff0c;学习计算机视觉既是一场冒险&#xff0c;也是一场盛宴。让我作为一位…

安科瑞诚邀您参加2024年CDCE国际数据中心及云计算展

展会邀请 安科瑞&#xff08;Acrel&#xff09;诚挚邀请您和您的公司代表参观我们在2024年CDCE国际数据中心及云计算展上的展位。 安科瑞&#xff08;Acrel&#xff09;是一家集研发、生产、销售和服务于一体的企业&#xff0c;成立于2003年并于2012年在创业板上市&#xff0…

探索机器人快换盘技术的未来之路:智能化与协作的革新

在当今快速发展的科技时代&#xff0c;机器人已成为众多领域不可或缺的得力助手。其中&#xff0c;机器人快换盘技术作为提升机器人灵活性和应用广度的重要技术&#xff0c;正经历着前所未有的变革与创新。下面请随我们一起深入探讨这一技术的未来发展趋势。 一、人工智能&…

建投数据获得安防工程企业设计施工维护能力证书(壹级)

近日&#xff0c;经中国安全防范产品行业协会审核&#xff0c;建投数据顺利获得安防工程企业设计施工维护能力证书&#xff08;壹级&#xff09;。 安防工程企业设计施工维护资质是对企业安防工程设计、施工、维护等领域专业能力的综合认证&#xff0c;是衡量企业规模、工程业绩…

阿里云百炼SFT微调实践

1. 场景识别 用户进行SFT前需要对应用场景进行识别和分析&#xff0c;一般进行模型微调的诉求是希望提升对应业务场景的能力&#xff0c;用户需要明确哪些能力是需要进行重点提升的。 场景划分上来讲&#xff0c;以垂类场景进行划分可分为教育、医疗、金融、法律、电商、旅游、…

IP地址的打卡路径是什么?

众多周知&#xff0c;IP地址使我们浏览网站的“必需品”&#xff0c;他会在我们进行网络活动时起到通关文牒一般的作用。那么&#xff0c;放我们进行网络活动时&#xff0c;我们的“通关文牒”上面会在哪些地点留下痕迹&#xff0c;IP地址的流转路径是什么&#xff1f; 第一关…