当前位置: 首页 > news >正文

【数据结构】Map 和 Set

目录

  • 二叉搜索树
    • 二叉搜索树---查找
    • 二叉搜索树---插入
    • 二叉搜索树---删除
  • Map和Set
    • Map的使用
    • Set的使用
  • 哈希表
    • 哈希冲突
    • 冲突避免
    • 冲突解决
      • 冲突解决---闭散列
      • 冲突解决---开散列
  • 题目练习
    • 只出现一次的数
    • 复制带随机指针的链表
    • 宝石与石头
    • 旧键盘

二叉搜索树

二叉搜索树也叫二叉排序树,具有以下性质:

  • 若它的左子树不为空,则左子树的所有结点的值都小于根结点的值。
  • 若它的右子树不为空,则右子树的所有结点的值都大于根结点的值。
  • 左右子树都是二叉搜索树。

例如:
搜索树

二叉搜索树—查找

二叉搜索树和普通二叉树的结构都一样,只是结点的值不太一样。

//二叉搜索树结构
static class TreeNode {public int val;public TreeNode left;public TreeNode right;public TreeNode(int val) {this.val = val;}
}public TreeNode root = null;

如何在二叉搜索树中查找某一结点值?我们可以根据二叉搜索树的性质,如果要查找的值大于根结点的值就继续往右边查找,如果要查找的值小于根结点的值就继续往左边查找。
查找过程
查找代码:

public TreeNode find(int val) {TreeNode cur = root;while (cur != null) {//大于往右边if (val > cur.val) {cur = cur.right;} else if (val < cur.val) {//小于往左边cur = cur.left;} elsereturn cur;}return null;
}

二叉搜索树—插入

插入
插入代码:

public boolean insert(int val) {TreeNode node = new TreeNode(val);//空树if (root == null) {root = node;return true;}TreeNode cur = root;TreeNode parent = null;while (cur != null) {if (val == cur.val) {return false;} else if (val < cur.val) {parent = cur;cur = cur.left;} else {parent = cur;cur = cur.right;}}//cur为空了,小于放左边,大于放右边if (val < parent.val) {parent.left = node;} else {parent.right = node;}return true;
}

二叉搜索树—删除

假设待删除结点为 cur,待删除结点的双亲结点为 parent,那么有下面几种情况:

  1. cur.left 为空
    cur的左为空

  2. cur.right 为空
    cur的右为空

  3. cur.left 不为空 并且 cur.right 不为空
    左右不为空

删除代码:

public void remove(int val) {TreeNode cur = root;TreeNode parent = null;//循环遍历找到要删除的结点while (cur != null) {if (cur.val == val) {//找到后,判断当前节点的情况removeNode(parent, cur);return;} else if (cur.val < val) {parent = cur;cur = cur.right;} else {parent = cur;cur = cur.left;}}
}//根据结点情况删除结点
private 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;}}
}

Map和Set

map 和 set 是一种专门用来进行搜索的容器或者数据结构,属于动态查找,也就是在查找时可以进行插入和删除操作。

一般把搜索的数据称为关键字(Key),和 Key 对应的称为值(Value),将其称为 Key-value 的键值对。分为以下两种:

  1. 纯 key 模型(Set): 例如:某单词是否在一句话中。
  2. Key-Value 模型(Map): 例如:某单词出现的次数 <单词,出现次数>。

Map的使用

put(K key, V value),设置 key 对应的 value

put方法

使用 put 方法时,插入的 key 一定不能是 null,否则会空指针异常,但是 value 可以。

key不能为null

插入的 key 一定是可以比较的,否则会报错。

如果 key 相同,则会替换原来的 value。

会替换原来的value

get(Object key) 返回 key 对应的 value
getOrDefault(Object key, V defaultValue) 返回 key 对应的 value,key 不存在,返回默认值

get 和 getOrdefalut

remove(Object key) 删除 key 对应的映射关系

remove方法

Set<K> keySet() 返回所有 key 的不重复集合
Collection<V> values() 返回所有 value 的可重复集合

KeySet 和 values方法

Set<Map.Entry<K, V>> entrySet() 返回所有的 key-value 映射关系

在这里插入图片描述

containsKey(Object key) 判断是否包含 key
containsValue(Object value) 判断是否包含 value

containskey和value

Set的使用

set方法使用
set方法使用

哈希表

通过某种函数使元素的存储位置与它的关键码之间建立映射关系的一种存储结构叫做哈希表(散列表)。

插入元素: 根据待插入元素的关键码,使用函数计算出该元素的存储位置并按此位置进行存放。
搜索元素: 对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功。

例如:
按照哈希函数插入

哈希冲突

假设现在要插入 23,就会出现冲突:
哈希冲突

不同关键字通过相同的哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞

注意: 哈希表底层数组的容量往往小于实际要存储的数量,这就导致一个问题,冲突的发生是必然的,我们只能尽量的低冲突。


冲突避免

设计合理的哈希函数可以减小冲突的概率。哈希函数设计原则:

  • 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有 m 个地址时,其值域必须在 0 到 m-1 之间。
  • 哈希函数计算出来的地址能均匀分布在整个空间中。
  • 哈希函数应该比较简单。

常见哈希函数:

  1. 直接定制法
    取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B
    优点:简单、均匀
    缺点:需要事先知道关键字的分布情况
    使用场景:适合查找比较小且连续的情况

  2. 除留余数法
    设散列表中允许的地址数为 m,取一个不大于 m,但最接近或者等于 m 的质数 p 作为除数,按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址。

还有几个不常用的:平方取中法、折叠法、随机数法、数学分析法。


负载因子调节: α(负载因子) = 元素个数 / 表的长度
负载因子越大,产生冲突的可能性就越大,所以要想减小负载因子,就要增大表的长度(扩容)。


冲突解决

解决哈希冲突两种常见的方法是:闭散列和开散列

冲突解决—闭散列

闭散列: 也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把 key 存放到冲突位置中的 “下一个” 空位置中去。

寻找下一个空位置:

  1. 线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。
    线性探测
    如果再插入 33,那几个数据就会在一块,那就需要其他方法解决这个问题。

  2. 二次探测
    线性探测的缺陷是产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨着往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法为:Hiii= ( H000+iii2)% m,i 等于1,2,3……。

二次探测

冲突解决—开散列

开散列法又叫链地址法(开链法),同样先计算出散列地址,把相同地址的关键码放于同一子集合,每一个子集合称为一个,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。类似下面这种:

冲突解决---开散列
开散列中每个桶中放的都是发生哈希冲突的元素,开散列就是把一个在大集合中的搜索问题转化为在小集合中做搜索了。我们还可以继续优化在小集合中搜索,例如:每个桶的背后是另一个哈希表或者每个桶的背后是一棵搜索树


题目练习

只出现一次的数

题目链接:【leetcode】136. 只出现一次的数字
题目描述:题目描述

这题我们利用 set 的性质来做,使用异或(将数组里的元素全部异或,最后结果就是只出现一次的数字)或者 map(统计每个元素出现的次数,value 为1的就是只出现一次的数字)也可以。

题解过程
代码:

class Solution {public int singleNumber(int[] nums) {Set<Integer> set = new HashSet<>();for(int x : nums){//利用add函数的返回值,返回false,//说明set中已经存在这个元素,那我们就移除if(!set.add(x)){set.remove(x);}}//最后 set 中就剩下只出现一次的数字for (Integer x : set) {return x;}return -1;}
}

复制带随机指针的链表

题目链接:【leetcode】138. 复制带随机指针的链表
题目描述:题目描述

题目的意思就是,我们需要根据题目给的链表,生成同等个数并且结点的 val 相等,所给链表的 next 和 random 域指向链表中的哪个结点,我们就需要在自己生成的链表中指向位置相对应的结点。

题解

为了达到题目要求,我们可以使用 map 来存储他们对应位置的结点。

过程

代码:

public Node copyRandomList(Node head) {Map<Node, Node> map = new HashMap<>();Node cur = head;//遍历链表while (cur != null) {//生成和题目的链表节点 相同值 的结点Node node = new Node(cur.val);//再把题目中链表的结点  和  生成的新结点存储到 map中map.put(cur, node);cur = cur.next;}//再次指向题目链表的头结点cur = head;while (cur != null) {//获取对应位置关系的结点map.get(cur).next = map.get(cur.next);map.get(cur).random = map.get(cur.random);cur = cur.next;}//新链表的头结点对应老链表头结点的valuereturn map.get(head);
}

宝石与石头

题目链接:【leetcode】771. 宝石与石头
题目描述:题目描述

我们可以把宝石都放到 set 中,然后遍历石头,如果 set 中有这个元素,那就是宝石,否则就不是。

代码:

class Solution {public int numJewelsInStones(String jewels, String stones) {Set<Character> set = new HashSet<>();//先把宝石加到 set中for (int i = 0; i < jewels.length(); i++) {set.add(jewels.charAt(i));               }int count = 0;//遍历石头,如果 set中包含,就是宝石for (int i = 0; i < stones.length(); i++) {if (set.contains(stones.charAt(i))) {count++;}}return count;}
}

旧键盘

题目链接:【牛客网】旧键盘
题目描述:题目描述

和上题类似,我们把坏键(_hs_s_a_es) 输出的字符串添加到 set 中,在遍历好键(7_This_is_a_test) 输出的字符串,如果没有哪个字符,那个键就是坏的,也就是我们要输出的。

代码:

public static void func(String str1,String str2) {Set<Character> set = new HashSet<>();//把坏键先转成大写,然后存储到 set中for (char c : str2.toUpperCase().toCharArray()) {set.add(c);}Set<Character> res = new HashSet<>();//遍历好键,把不存在的添加到 res中for (char c : str1.toUpperCase().toCharArray()) {//!res.contains(c) 避免重复输出 if (!set.contains(c) && !res.contains(c)) {res.add(c);System.out.print(c);}}
}

相关文章:

【数据结构】Map 和 Set

目录二叉搜索树二叉搜索树---查找二叉搜索树---插入二叉搜索树---删除Map和SetMap的使用Set的使用哈希表哈希冲突冲突避免冲突解决冲突解决---闭散列冲突解决---开散列题目练习只出现一次的数复制带随机指针的链表宝石与石头旧键盘二叉搜索树 二叉搜索树也叫二叉排序树&#x…...

IPVlan 详解

文章目录简介Ipvlan2同节点 Ns 互通Ns 内与宿主机 通信第三种方法Ns 到节点外部结论Ipvlan31. 同节点 Ns 互通Ns 内与宿主机 通信Ns 内到外部网络总结源码分析ipvlan 收包流程收包流程主要探讨使用 ipvlan 为 cni 通过虚拟网卡的实现。简介 ipvlan 和 macvlan 类似&#xff0c…...

直播间的2个小感悟

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 在线人数固定 最近直播间出现了很多新面孔&#xff0c;有的是偶然刷到的&#xff0c;有的是关注互联网找到的。而直播间的人数一直没什么变化&#xff0c;卢松松在抖音直播较少&#xff0c;主播间…...

STM32开发(15)----芯片内部温度传感器

芯片内部温度传感器前言一、什么是内部温度传感器&#xff1f;二、实验过程1.STM32CubeMX配置2.代码实现3.实验结果总结前言 本章介绍STM32芯片温度传感器的使用方法和获取方法。 一、什么是内部温度传感器&#xff1f; STM32 有一个内部的温度传感器&#xff0c;可以用来测…...

Apache Hadoop生态部署-zookeeper分布式安装

目录 查看服务架构图-服务分布、版本信息 一&#xff1a;安装前准备 1&#xff1a;zookeeper安装包选择--官网下载 2&#xff1a;zookeeper3.5.7安装包--百度网盘 二&#xff1a;安装与常用配置 2.1&#xff1a;下载解压zk安装包 2.2&#xff1a;配置环境变量 2.3&#x…...

MySQL(九)

mysql的锁机制 1、MySQL锁的基本介绍 ​ **锁是计算机协调多个进程或线程并发访问某一资源的机制。**在数据库中&#xff0c;除传统的 计算资源&#xff08;如CPU、RAM、I/O等&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发访问的一…...

Matlab 计算一条直线与一条线段的交点

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 这里假设一条直线的方向为 ( a , b , c ) (a,b,c) (a,b,...

Read book Netty in action(Chapter VI)--ByteBuf

序言 之前学习了传输&#xff0c;通过前面的学习我们都知道&#xff0c;网络数据的基本单位是字节。JDK中提供了ByteBuffer作为字节的容器&#xff0c;但是过于繁琐复杂&#xff0c;Netty中提供了ByteBuf作为替代品。学习一下。 API Netty的数据处理API通过两个组件暴露 ---…...

VsCode开发工具的入门及基本使用

VsCode开发工具的入门及基本使用一、VsCode介绍1.VsCode简介2.VsCode特点二、安装VsCode1.下载VsCode2.安装VsCode3.打开VsCode三、设置VsCode中文1.搜索中文语言插件2.安装中文语言插件四、初识VsCode1.VsCode左侧栏模块2.系统设置功能五、VsCode初始配置1.禁用自动更新2.开启…...

python标准库——OS模块接口详解

OS系统操作模块 os模块提供各种Python 程序与操作系统进行交互的接口 os模块是整理文件和目录最常用的模块 函数作用补充os.sep()取代操作系统特定的路径分隔符os.name()指示你正在使用的工作平台。比如对于Windows&#xff0c;它是nt&#xff0c;而对于Linux/Unix用户&…...

LeetCode 622.设计循环队列

设计你的循环队列实现。 循环队列是一种线性数据结构&#xff0c;其操作表现基于 FIFO&#xff08;先进先出&#xff09;原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里&a…...

OraDump导出套件

OraDump导出套件 只需单击几下即可将数据从Oracle转储文件导出到流行的数据库和格式。 OraDump Export Kit是一个将数据从Oracle转储文件导出到流行数据库和格式的软件包。该产品具有高性能&#xff0c;因为它直接读取转储文件。命令行支持允许编写脚本、自动化和安排转换过程。…...

CVE-2022-22947 SpringCloud GateWay SPEL RCE 漏洞分析

漏洞概要 Spring Cloud Gateway 是Spring Cloud 生态中的API网关&#xff0c;包含限流、过滤等API治理功能。 Spring官方在2022年3月1日发布新版本修复了Spring Cloud Gateway中的一处代码注入漏洞。当actuator端点开启或暴露时&#xff0c;可以通过http请求修改路由&#xff…...

Firebase常用功能和官方Demo简介

一、Firebase简介Firebase刚开始是一家实时后端数据库创业公司&#xff0c;它能帮助开发者很快的写出Web端和移动端的应用。自2014年10月Google收购Firebase以来&#xff0c;用户可以在更方便地使用Firebase的同时&#xff0c;结合Google的云服务。现在的Firebase算是谷歌旗下的…...

MATLAB R2020a 与PreScan8.5.0 详细安装教程(图文版)

目录MATLAB安装PreScan安装每文一语MATLAB安装 MATLAB是一款数学软件&#xff0c;用于科学计算、数据分析和可视化等任务。以下是MATLAB的几个优势&#xff1a; 丰富的工具箱&#xff1a;MATLAB拥有多种工具箱&#xff0c;包括信号处理、图像处理、优化、控制系统等&#xff0…...

CNI 网络流量 4.3 Calico felix

文章目录felix 太重要了&#xff0c;单独一文搞懂它Felix是一个守护程序&#xff0c;在每个 endpoints 的节点上运行。Felix 负责编制路由和 ACL 规则等&#xff0c;以便为该主机上的 endpoints 资源正常运行提供所需的网络连接 主要实现一下工作 管理网络接口&#xff0c;Feli…...

超声波风速风向传感器的通讯协议

接线定义 1 电源正 棕色线 4 风向信号 2 电源负 黑色线 5 485A 蓝色线 3 风速信号 6 485B 灰色线 ⊙寄存器参数表 地址 访问权限 参数名称 数据解析方法 0x0000 R 风速 瞬时 *100 上报 0x0001 R 风向 原数上报 0x0002 R 最大风速 *100 上报 0x0003 R 平均风速 *100 上报 0x000…...

JVM笔记(8)—— 直接内存

一、什么是直接内存 直接内存不是虚拟机运行时数据区的一部分&#xff0c;是在运行时数据区外、直接向系统申请的内存空间。 通常&#xff0c;访问直接内存的速度会优于堆&#xff0c;读写性能更好。因此&#xff0c;出于性能考虑&#xff0c;读写频繁的场合可能会考虑使用直…...

Unity性能优化:如何优化Drawcall

前言 降低游戏的Drawcall&#xff0c;是渲染优化很重要的手段&#xff0c;接下来从以下4个方面来分析如何降低DrawCall: 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;希望大家可以点击进来一起交流一下开发经验呀 降低Drawcall的意义是什么?如何查看游戏的Drawca…...

类与对象(this 关键字、构造器)

目录一、面向对象二、类与对象三、对象内存图四、成员变量和局部变量区别五、this关键字六、构造器/构造方法一、面向对象 一种编程思想:也就是说我们要以何种思路&#xff0c;解决问题&#xff0c;以何种形式组织代码 当解决一个问题的时候&#xff0c;面向对象会把事物抽象成…...

[NOIP2002 普及组] 过河卒

题目描述&#xff1a; 棋盘上 A 点有一个过河卒&#xff0c;需要走到目标 B 点。卒行走的规则&#xff1a;可以向下、或者向右。同时在棋盘上 C 点有一个对方的马&#xff0c;该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。 棋盘用坐标表…...

redis事务和锁机制

目录 事务定义 事务操作命令 redis事务的错误处理 redis事务冲突问题 redis解决事务冲突的方法 Redis事务的三个特性 事务定义 redis事务是一个单独的隔离操作&#xff1a;事务中的所有命令都会序列化&#xff0c;按顺序的执行。事务中在执行过程中&#xff0c;不会被其他客户…...

Java实例——线程

1、查看线程存活状态 Thread.isAlive() Thread.getName() public class MyThread extends Thread{Overridepublic void run() {for (int i 0; i < 10; i) {printMsg();}}public static void printMsg(){Thread thread Thread.currentThread();//Thread.getName() 获取线程…...

云计算学习课程——越来越重要的云安全

2023&#xff0c;越来越多的企业和组织正在或即将把核心系统和数据迁移上云端&#xff0c;其中以公有云和服务居多&#xff0c;那么就意味着在数据迁移的过程中会出现安全问题的几率更大。企业也越来越注重云安全体系&#xff0c;对我们云计算运维工程师来说&#xff0c;也是一…...

Android 高性能列表:RecyclerView + DiffUtil

文章目录背景介绍一般刷新 notifyDataSetChanged()局部刷新实现调用代码准备工作创建 MyDiffUtilCallback 类继承 DiffUtil.Callback 抽象类MyAdpter 类代码实现步骤总结通过 log 证实 diffutil 的局部刷新diffutil 优化后台线程参考主线程参考diff 更新优化后写法相关参考背景…...

为什么派生类的构造函数必须在初始化列表中调用基类的构造函数

调用派生类的构造函数时&#xff0c;可能会调用继承自基类的函数&#xff0c;也就可能会用到基类的数据成员&#xff0c;因此&#xff0c;调用派生类的构造函数时&#xff0c;必须确保继承自基类的数据成员已构造完毕&#xff0c;而将基类构造函数的调用写在初始化列表中&#…...

2023年2月初某企业网络工程师面试题【建议收藏】

拓扑图如下&#xff0c;主机A与主机B能互相通信&#xff0c;但是A不能ping通RA的F0接口&#xff0c;这是为什么&#xff1f;RA上f0接口上配置了ACL&#xff0c;禁止源ip为主机A&#xff0c;目的ip为RA f0的数据包的发送&#xff1b; 第一个路由器上只有到主机B网段的路由&#…...

分布式下(sso)单点登录

目录标题一、基于rediscookie的单点登录二、基于jwtcookie的单点登录一、基于rediscookie的单点登录 传统单机应用登录 传统单机应用&#xff0c;一般是结合session和cookie实现认证、授权。用户通过输入账号密码登录系统&#xff0c;登录成功后在系统创建一个session来保存用…...

PMP真的有那么厉害?你需要考PMP吗?

这个含金量是有的&#xff0c;是目前项目管理界含金量较高的证书&#xff0c;但也要分人&#xff0c; 因为这是职业证书&#xff0c;主要用于提高职场工作能力&#xff0c;不搞这一行的&#xff0c;PMP证书含金量再高也是一张废纸&#xff0c;可以看下下面这张图&#xff0c;这…...

高通平台开发系列讲解(WIFI篇)802.11 基本概念

文章目录 一、WLAN概述二、802.11发展历程三、802.11基本概念沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本文将基于高通平台介绍802.11基本概念。 一、WLAN概述 WLAN是Wireless Local Area Network的简称,指应用无线通信技术将计算机设备互联起来,构成可以互相通…...