Tree搜索二叉树、map和set_数据结构

数据结构专栏
如烟花般绚烂却又稍纵即逝的个人主页
本章讲述数据结构中搜索二叉树与HashMap的学习,感谢大家的支持!欢迎大家踊跃评论,感谢大佬们的支持!
在这里插入图片描述

目录

  • 搜索二叉树的概念
    • 二叉树搜索模拟实现
        • 搜索二叉树查找
        • 搜索二叉树插入
        • 搜索二叉树删除
  • HashMap 和HashSet的定义
    • hashCode(开散链法)
    • 哈希冲突
        • 模拟实现哈希桶(尾叉法)
        • 模拟创建泛型哈希桶

搜索二叉树的概念

二叉树左边的值小与根节点,右边的值大于根节点。
左树<根节点<右树
这样也大大提升了我们代码搜索的效率
这时通过中序遍历得到一个有序的数组。
在这里插入图片描述

二叉树搜索模拟实现

搜索二叉树查找

给一个值来判断数组中是否包含这个元素。
思想:1.节点不能为空,为空说明没有元素
2.明确左边的数都比跟节点要小,而右边的树比跟节点大,小往左走,大往右走,搜索到返回true,出了条件返回false(没有搜索到此元素)。
在这里插入图片描述

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

时间复杂度:最好情况下O(logN)
最坏情况下O(N)——在单分枝的情况下

搜索二叉树插入

如果要插入数据的情况下,插入的数据大于根节点一直走,如果小于根节点则插入左树,如果一直大于则直到为空插入,因为遍历的cur为空出来后没有最后一个节点的位置,在插入之前需要定义一个parent来记录根节点的,来放到左树或者右树。
在这里插入图片描述

  public boolean insert(int val){
        if(this.root==null){
            //root为空情况下
            this.root=new TreeNode(val);
            return true;
        }
        TreeNode cur=this.root;
        //定义一个值来接收前一个父亲节点值
        TreeNode parent=null;
        while(cur!=null){
            parent=cur;
            if(cur.val>val){
                parent=cur;
                cur=cur.left;
            } else if (cur.val< val) {
                parent=cur;
                cur=cur.right;
            }else{
                //不需两个相同元素存在
                return false;
            }
        }
        TreeNode node=new TreeNode(val);
        if(parent.val>val){
            //插入左边
            parent.left=node;
        }else{
            parent.right=node;
        }
        return true;
    }
搜索二叉树删除

当我们要删除树中某一个节点的时候我们要考虑的情况比较多当left为空节点.
这里分三种情况讨论
第一种情况:
1.如果想要删除的节点为root,且左树为空情况下,则直接指向right。
因为parent事cur的上一个节点的记录
2.如果parent的左树为cur且cur的左树为空,则parent.left=cur.right。
3.如果parent的右树是cur且cur的左树为空,则parent.right=cur.right。

在这里插入图片描述

第二种情况:
1当cur==root时且没有右树,则左树为新的根节点。
2.parent指向的左树为cur时,因为cur没有右树则parent.left=cur.left。
3.parent指向的右树为cur时,因cur没有右树则parent.right=cur.left。

在这里插入图片描述

第三种情况:要删除的cur的左右子树都不为空。
如果都不为空,需要找到比左树大,但是比右树要小的值。
如果cur的下一个右树节点的左树存在最小值则直接将其cur覆盖。或者cur的右树没有左树节点,则右树第一个节点覆盖掉cur。

cur的左右树都不为空,则cur是比左树要到,所以不需要考虑cur的左树部分。
定义两个值来标记targetParent和target的右树,从右树的左侧找到最小值,进行覆盖(curParent来作为上一个的父亲节点),这时候要删除target节点,当targetParent的左树为target时,因为target一定是在最后一个左树位置,target的右树赋值给targetParent来取消与target的连接。
如果target为右树则targetParent直接指向target的下一个右树。

  public void  removeThisNode(TreeNode cur,TreeNode parent){
        if(cur.left==null){
            //如果cur根节点为要删除的节点,直接将root指向cur.right
        if(cur==this.root){
            this.root=cur.right;
        } else if (cur==parent.left) {
            parent.left=cur.right;
        }else{
            parent.right=cur.right;
        }
        } else if (cur.right==null) {
            if(cur==root){
                this.root=cur.left;
            } else if (cur==parent.left) {
            parent.left=cur.left;
            }else{
            parent.right=cur.left;
            }
        }else{
            TreeNode targetParent=cur;
            TreeNode target=cur.right;
            while(target.left!=null){
                targetParent=target;
                target=target.left;
            }
            //cur的值被target覆盖掉
            cur.val=target.val;
            //这里target的指向修改
            if(targetParent.left==target){
                targetParent.left=target.right;
            }else{
                targetParent.right=target.right;
            }
        }
    }

时间复杂度O(logN)

HashMap 和HashSet的定义

HashSet是java集合框架中Set的常用类。
HashSet不允许存储重复元素的集合,它使用哈希表来存储元素(val),确保每一个元素是唯一的,时间复杂度为O(1).

HashMap是java集合框架Map的常用类。
HashMap也是用哈希表通过key键值来存储val,与HashMap不同,它通过key键值来存储,元素可以相同,但是key键值一单相同val值就会被覆盖掉。

hashCode(开散链法)

二叉搜索树是通过对半切的方式进行比较,类似于我们的二分法查找,但是它的如果是两边的树是平衡时,时间复杂度是O(logN),如果是一棵单分枝树时间复杂度来到O(N)都要进行遍历。

而哈希可以不经过比较,一次性从想要查找的范围内获取到该元素。
如果想要实现,通过哈希函数使元素的存储位置与它的关键码之间能够建立一一映射的关系,通过公式:hash=key(查找的下标)%capacity(容量)如下图所示:

5%10=5将19到5下标
13%10=3将24放到3下标
15%10=5将52放到5下标的链表的next处
在这里插入图片描述

哈希冲突

而通过上述我们发现,5,15都已经放到了5下标位置,不同关键字通过相同的哈希函数计算出相同的哈希地址
如何避免哈希冲突?
哈希冲突无法直接避免,因为key我们的元素是不能改变的,我们只能控制空间的大小,来减少链表中元素
负载因子 = 表中有效数据个数 / 空间的大小。
>负载因子越大,产出冲突的概率越高,增删查改的效率越低。
负载因子越小,产出冲突的概率越低,增删查改的效率越高

负载因子设定为0.75
当我们的元素个数/空间的长度如果超出0.75,则需要扩容,加大空间,将负载因子缩小,从而让查改效率加强。
在这里插入图片描述

模拟实现哈希桶(尾叉法)

1.这里我们创建一个哈希表来进行搜索,通过数组和链表的方式来构成,让其更快搜索到想要搜索到的值,将下标值放入指定的数组链表中来存储。
2.每个数组的下标对应一个链表,当想要搜索某个下标值时,hash=key%capacity 得到的就是某一下标的链表,通过链表连接该key的每一个元素。
3.判断是否需要大于负载因子(扩容 )
当我们扩容时,通过2*len的长度进行扩容,然后通过key%新定义的数组的容量,给到新的数组链表中,将之前存储的链表指向其他下一个节点即可。
在这里插入图片描述

public class HashBucket {
//内部类
    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 Node[] array;
    int size;//长度
    public static final float loadFactor=0.75f;//负载因子的默认值
    public HashBucket(){
    //构造方法
        this.array=new Node[10];
    }
    //放入元素
 public void put(int key,int val){
        int index=key%array.length;
        Node node=new Node(key,val);
        Node preV=null;
        //为空
        if(this.array[index]==null){
            this.array[index]=node;
        }else{
            //不为空
            Node cur=this.array[index];
            while(cur!=null){
                if(cur.key==key){
                    cur.val=val;
                    return;
                }
                preV=cur;
                cur=cur.next;
            }
            assert preV!=null;
             preV.next=node;
        }
        size++;
        if(judgThisLoadFactorFull())
            //扩容
            grow();
    }
    //判断是否大于负载因子
     public boolean judgThisLoadFactorFull(){
        return size*1.0f/array.length>loadFactor;
    }
    //扩容
      private void grow() {
        Node[] newArray=new Node[2*array.length];//两倍的扩容
        for(int i=0;i<array.length;i++){
            Node cur=this.array[i];
            Node preV=null;
            while(cur!=null){
                int index= cur.key%newArray.length;
                if(newArray[index]==null){
                    newArray[index]=cur;
                    cur=cur.next;
                    this.array[i]=cur;
                    newArray[index].next=null;
                }else{
                   Node newCur=newArray[index];
                   while(newCur!=null){
                       preV=newCur;
                       newCur=newCur.next;
                   }
                   assert preV!=null;
                   preV.next=cur;
                   cur=cur.next;
                   this.array[i]=cur;
                   preV.next.next=null;
                }
            }
        }
        this.array=newArray;
    }
    //通过key搜索到值
    public int getValByThisKey(int key){
        int index=key%this.array.length;
        Node cur=this.array[index];
        while (cur!=null){
            if(cur.key==key){
                return cur.val;
            }
            cur=cur.next;
        }
        return -1;
    }

当我们创建了一个泛型的类型后,生成了hashCode方法,我们生成两个参数相同的对象时,如果将student1放入到hashmap中,当我们想要查找hashmap1中的值时,使用student2也可以查到student1中的值,这里的哈希函数通过计算关键码,因为两者的关键码相同,得到关键码的数值。

 Student student1=new Student("12313");
        Student student2=new Student("12313");
        System.out.println(student1.hashCode());
        System.out.println(student2.hashCode());
        HashMap<Student,Integer> hashMap=new HashMap<>();
        hashMap.put(student1,2);
        System.out.println(hashMap.get(student2));
       

在这里插入图片描述
而我们如何自己模拟实现一个自定义类型的哈希桶呢?
接下来我们实现以下

模拟创建泛型哈希桶

这里泛类型哈希桶实现与上述哈希桶实现类似,但是这里注意get方法中的比较关键码是通过equals来判断

public class HashBucket<K,V>{
    static class Node<K,V>{
        public K key;
        public V val;
        public Node<K,V> next;

        public Node(K key, V val) {
            this.key = key;
            this.val = val;
        }
    }
    public Node<K,V>[] array;
    public int usedSize;
    public static final float DefaultFactor=0.75f;
    
    public HashBucket(){
        this.array=(Node<K, V>[]) new Node[10];
    }
    //放入值
    public void put(K key,V val){
        int hash=key.hashCode();
        int index=hash%array.length;
        Node<K,V> cur=array[index];
        Node<K,V> node=new Node<>(key,val);
        Node<K,V> preV=null;
        if(cur==null){
            this.array[index]=node;
        }else{
            while(cur!=null){
                if(cur.key==key){
                    cur.val=val;
                }
                preV=cur;
                cur=cur.next;
            }
            preV.next=node;
        }
        usedSize++;
    }
    //获取值
     public V get(K key){
        int hash= key.hashCode();
        int index=hash % array.length;
        Node<K,V> cur=this.array[index];
        while(cur!=null){
        //这里比较不是==
            if(cur.key.equals(key)){
                return cur.val;
            }
            cur=cur.next;
        }
         return null;
     }
}

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

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

相关文章

分离整数的各个数

分离整数的各个数 C语言代码C 语言代码Java语言代码Python语言代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 给定一个整数&#xff0c;要求从个位开始分离出它的每一位数字。 输入 输入一个整数&#xff0c;整数在1到100000000之间…

OpenAI Whisper 语音识别 模型部署及接口封装

环境配置: 一、安装依赖&#xff1a; pip install -U openai-whisper 或者&#xff0c;以下命令会从这个存储库拉取并安装最新的提交&#xff0c;以及其Python依赖项&#xff1a; pip install githttps://github.com/openai/whisper.git 二、安装ffmpeg&#xff1a; cd …

PotPlayer 最新版本支持使用 Whisper 自动识别语音生成字幕

PotPlayer 最新版本支持使用 Whisper 自动识别语音生成字幕 设置使用下载地址 设置 使用 下载地址 https://www.videohelp.com/software/PotPlayer

第33周:运动鞋识别(Tensorflow实战第五周)

目录 前言 一、前期工作 1.1 设置GPU 1.2 导入数据 1.3 查看数据 二、数据预处理 2.1 加载数据 2.2 可视化数据 2.3 再次检查数据 2.4 配置数据集 2.4.1 基本概念介绍 2.4.2 代码完成 三、构建CNN网络 四、训练模型 4.1 设置动态学习率 4.2 早停与保存最佳模型…

云轴科技ZStack助力 “上科大智慧校园信创云平台”入选上海市2024年优秀信创解决方案

近日&#xff0c;为激发创新活⼒&#xff0c;促进信创⾏业⾼质量发展&#xff0c;由上海市经济信息化委会同上海市委网信办、上海市密码管理局、上海市国资委等主办的“2024年上海市优秀信创解决方案”征集遴选活动圆满落幕。云轴科技ZStack支持的“上科大智慧校园信创云平台”…

Linux 服务器使用指南:诞生与演进以及版本(一)

一、引言 在当今信息技术的浪潮中&#xff0c;Linux 操作系统无疑是一个关键的支柱&#x1f60e;。无论是在服务器管理、软件开发还是大数据处理领域&#xff0c;Linux 都以其卓越的适应性和优势脱颖而出&#x1f44d;。然而&#xff0c;对于许多新手而言&#xff0c;Linux 系统…

基于树莓派3B+的简易智能家居小项目(WiringPi库 + C语言开发)

github主页&#xff1a;https://github.com/snqx-lqh 本项目github地址&#xff1a;https://github.com/snqx-lqh/RaspberryPiSmartHome 硬件开源地址&#xff1a;https://oshwhub.com/from_zero/shu-mei-pai-kuo-zhan-ban 欢迎交流 树莓派智能家居项目&#xff0c;学习树莓派的…

YOLOv8-Pose NCNN安卓部署

YOLOv8-Pose NCNN安卓部署 前言 YOLOv8-Pose NCNN安卓部署 目前的帧率可以稳定在30帧左右&#xff0c;下面是这个项目的github地址&#xff1a;https://github.com/gaoxumustwin/ncnn-android-yolov8-pose 介绍 在做YOLOv8-Pose NCNN安卓部署的时候&#xff0c;在github上…

【热门主题】000077 物联网智能项目:开启智能未来的钥匙

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【热…

【STM32学习】TB6612FNG驱动芯片的学习,驱动电路的学习

目录 1、TB6612电机驱动芯片 1.1如下是芯片的引脚图&#xff1a; 1.2如下图是电机的控制逻辑&#xff1a; 1.3MOS管运转逻辑 1.3典型应用电路 2、H桥驱动电路 2.1、单极模式 2.2、双极模式 2.3、高低端MOS管导通条件 2.4、H桥电路设计 2.5、自举电路 3、电气特性 3…

day01(Linux底层)基础知识

目录 导学 基础知识 1、Bootloader是什么 2、Bootloader的基本作用 3、入式中常见的Bootloader有哪些 4、Linux系统移植为什么要使用bootloader 5、uboot和Bootloader之间的关系 6.Uboot的获取 7、uboot版本命名 8、uboot版本选择 9、uboot的特点 10.Uboot使用 导学…

OpenFeign 服务调用

1.简介 微服务架构中使用 OpenFeign 进行服务调用&#xff0c; OpenFeign 提供了一种简洁的方式来定义和处理服 务间的调用。 OpenFeign 作为一个声明式的、模块化的 HTTP 客户端&#xff0c;通过 「接口」 的定义和 「注解」 的使用&#xff0c;简化了微服务之间的通信调用。…

superset load_examples加载失败解决方法

如果在执行load_examples命令后,出现上方图片情况,或是相似报错(url error\connection error),大概率原因是python程序请求github数据,无法访问. 因此我们可以将数据下载在本地来解决. 1.下载zip压缩文件,存放到本地 官方示例地址:GitHub - apache-superset/examples-data …

k8s集成skywalking

如果能科学上网的话&#xff0c;安装应该不难&#xff0c;如果有问题可以给我留言 本篇文章我将给大家介绍“分布式链路追踪”的内容&#xff0c;对于目前大部分采用微服务架构的公司来说&#xff0c;分布式链路追踪都是必备的&#xff0c;无论它是传统微服务体系亦或是新一代…

深度学习Python基础(2)

二 数据处理 一般来说PyTorch中深度学习训练的流程是这样的&#xff1a; 1. 创建Dateset 2. Dataset传递给DataLoader 3. DataLoader迭代产生训练数据提供给模型 对应的一般都会有这三部分代码 # 创建Dateset(可以自定义) dataset face_dataset # Dataset部分自定义过的…

【Git教程 之 安装】

Git教程 之 安装 Git教程 之 安装Windows系统安装gitLinux安装gitmacOS安装Git Git教程 之 安装 Windows系统安装git 首先从官网上直接下载安装 选择对应的操作系统&#xff0c;我电脑是Windows&#xff0c;所以我演示以Windows为主 点进去 点击Click here to download 下…

ElasticSearch学习记录

服务器操作系统版本&#xff1a;Ubuntu 24.04 Java版本&#xff1a;21 Spring Boot版本&#xff1a;3.3.5 如果打算用GUI&#xff0c;虚拟机安装Ubuntu 24.04&#xff0c;见 虚拟机安装Ubuntu 24.04及其常用软件(2024.7)_ubuntu24.04-CSDN博客文章浏览阅读6.6k次&#xff0…

【AI】数据,算力,算法和应用(3)

三、算法 算法这个词&#xff0c;我们都不陌生。 从接触计算机&#xff0c;就知道有“算法”这样一个神秘的名词存在。象征着专业、权威、神秘、高难等等。 算法是一组有序的解决问题的规则和指令&#xff0c;用于解决特定问题的一系列步骤。算法可以被看作是解决问题的方法…

Java代码操作Zookeeper(使用 Apache Curator 库)

1. Zookeeper原生客户端库存在的缺点 复杂性高&#xff1a;原生客户端库提供了底层的 API&#xff0c;需要开发者手动处理很多细节&#xff0c;如连接管理、会话管理、异常处理等。这增加了开发的复杂性&#xff0c;容易出错。连接管理繁琐&#xff1a;使用原生客户端库时&…

SAP SD学习笔记17 - 投诉处理3 - Credit/Debit Memo依赖,Credit/Debit Memo

上一章讲了 请求书&#xff08;发票&#xff09;的取消。 SAP SD学习笔记16 - 请求书的取消 - VF11-CSDN博客 再往上几章&#xff0c;讲了下图里面的返品传票&#xff1a; SAP SD学习笔记14 - 投诉处理1 - 返品处理&#xff08;退货处理&#xff09;的流程以及系统实操&#…