深入理解 并查集LRUCaChe
并查集&LRUCaChe

个人主页:顾漂亮
文章专栏:Java数据结构
1.并查集的原理
在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个单元素集合,然后根据一定规律将归于同一组元素的集合合并。在此过程中要反复运用到查询某一个元素归属于哪一个集合的运算。适合于描述这类问题的抽象数据类型称为并查集,(union - find set)并查集的底层运用的是数组。
举例:
1.初始情况:假设现在有10个元素(0 - 9),开始情况是每一个元素相互独立,每个元素自成一个单元素集合。

提问:为什么初始情况下数组元素全为-1?
在回答上述问题之前,我们应该先了解以下并查集的使用规则:
- 数组的下标对应集合中的元素,例如数组9下标对应的就是9这个元素
- 数组中的值如果为负数,负号表示根,数字代表该集合中元素的个数
- 数组中的值如果是非负数,代表该元素双亲结点在数组中的下标
根据上述规则,我们可以知道初始情况下10个元素,每个元素自成一个单元素集合,因此-1代表,每个单元素集合中只有一个元素,并且这个元素的根也是其本身。
2.**初次合并:**将单元素集合按照下图所示规律进行合并成三个集合

可以得到并查集图形为:

3.第二次合并:将单元素集合按照下图所示规律进行合并成三个集合

可以得到并查集图形为:

2.并查集可以解决的问题
- 查找元素属于哪一个集合
- 根据数组表示的树形关系向上寻找,一直找到根
- 查看两个元素是否属于同一个集合
- 比较两个集合的根是否相同,相同属于同一个集合,反之则不属于一个集合
- 将两个集合合并成一个集合
- 先将两个集合的根合并
- 集合的个数
- 遍历数组,数组中元素为非负数的个数即为集合的个数
3.并查集的代码实现
import java.util.Arrays;public class UnionFindSet {public int[] elem;//底层为数组public UnionFindSet(int n){//初始化数组大小为nelem = new int[n];//将数组中元素初始化为-1Arrays.fill(elem,-1);}//查找根public int findRoot(int val){if(val < 0){throw new IndexOutOfBoundsException("val不合法");}while(elem[val] >= 0){val = elem[val];}return val;}//合并操作public void union(int x1, int x2){//确定两个元素根节点int index1 = findRoot(x1);int index2 = findRoot(x2);//如果两个元素属于同一个集合if(index2 == index1){return;}//将两个集合根节点合并elem[index1] = elem[index1]+elem[index2];elem[index2] = index1;}//判断两个数字是不是在同一集合中public boolean isSameSet(int x1, int x2){//确定两个元素根节点int index1 = findRoot(x1);int index2 = findRoot(x2);if(index2 == index1){return true;}return false;}//求数组中集合的个数public int getCount(){int count = 0;for (int i = 0; i < elem.length; i++) {if(elem[i] < 0){count++;}}return count;}//打印数组public void printSet(){for (int i = 0; i < elem.length; i++) {System.out.print(elem[i] + " ");}System.out.println();}
}
4.并查集相关面试题
省份问题:
- 求解思路:
- 实现一个并查集
- 如果两个城市联通,放在一个集合中
- 返回并查集中元素小于0的个数即为省份数量
class Solution {public int findCircleNum(int[][] isConnected) {int n = isConnected.length;UnionFindSet ufs = new UnionFindSet(n);//行for(int i = 0; i < n; i++){//列for(int j = 0; j < isConnected[i].length; j++){if(isConnected[i][j] == 1){//合并ufs.union(i,j);} }}return ufs.getCount();}}class UnionFindSet {public int[] elem;//底层为数组public UnionFindSet(int n){elem = new int[n];//初始化为n大小的数组Arrays.fill(elem, -1);//将数组初始化为-1,注意,为什么初始化为-1?}//查找根public int findRoot(int val){if(val < 0){throw new IndexOutOfBoundsException("数据不合法");}//注意这个循环算法易错while(elem[val] >= 0){val = elem[val];}return val;}//合并操作public void union(int x1, int x2){int index1 = findRoot(x1);int index2 = findRoot(x2);//如果根相同,直接返回if(index2 == index1){return;}//注意执行顺序elem[index1] = elem[index1] + elem[index2];elem[index2] = index1;}//判断两个数字是不是在同一集合中public boolean isSameSet(int x1, int x2){int index1 = findRoot(x1);int index2 = findRoot(x2);if(index2 == index1){return true;}return false;}//求数组中集合的个数public int getCount(){int count = 0;for (int i = 0; i < elem.length; i++) {if(elem[i] < 0){count++;}}return count;}//打印数组public void printSet(){for (int i = 0; i < elem.length; i++) {System.out.print(elem[i] + " ");}System.out.println();}
}
等式方程可满足性:
- 解题思路:
- 将"=="两边数据放入一个集合中
- 检查"!="两边数据是否在同一个集合中,如果在返回false,如果不再返回true
class Solution {public boolean equationsPossible(String[] equations) {UnionFindSet set = new UnionFindSet(26);//一共有26个英文字母//1. 将“=”号左右元素合并成同一个集合for(int i = 0; i < equations.length; i++){if(equations[i].charAt(1) == '='){set.union(equations[i].charAt(0) - 'a', equations[i].charAt(3) - 'a');}}//2. 检查“!”左右两边是否在同一个集合中for(int i = 0; i < equations.length; i++){if(equations[i].charAt(1) == '!'){if(set.isSameSet(equations[i].charAt(0) - 'a', equations[i].charAt(3) - 'a')){return false;}}}return true;}
}class UnionFindSet {public int[] elem;//底层为数组public UnionFindSet(int n){elem = new int[n];//初始化为n大小的数组Arrays.fill(elem, -1);//将数组初始化为-1,注意,为什么初始化为-1?}//查找根public int findRoot(int val){if(val < 0){throw new IndexOutOfBoundsException("数据不合法");}//注意这个循环算法易错while(elem[val] >= 0){val = elem[val];}return val;}//合并操作public void union(int x1, int x2){int index1 = findRoot(x1);int index2 = findRoot(x2);//如果根相同,直接返回if(index2 == index1){return;}//注意执行顺序elem[index1] = elem[index1] + elem[index2];elem[index2] = index1;}//判断两个数字是不是在同一集合中public boolean isSameSet(int x1, int x2){int index1 = findRoot(x1);int index2 = findRoot(x2);if(index2 == index1){return true;}return false;}//求数组中集合的个数public int getCount(){int count = 0;for (int i = 0; i < elem.length; i++) {if(elem[i] < 0){count++;}}return count;}//打印数组public void printSet(){for (int i = 0; i < elem.length; i++) {System.out.print(elem[i] + " ");}System.out.println();}
}
LRUCaChe
1.概念解析:
1.1什么是LRU?
LRU(Last recently used)的缩写,意思是最近最少使用,是一种CaChe替换算法。
1.2什么是Cache?
狭义上:Cache是指位于CPU和主存间的快速RAM,通常它不像系统主存那样使用DRAM技术,而是用昂贵但是较为快速的SRAM技术
广义上:位于速度相差较大的两种硬件之间,用于协调两者数据传输速度差异的结构。处理CPU与主内存之间有Cache,内存与磁盘之间也有, 乃至在硬件与网络之间也有某种意义上的Cache–称为Internet临时文件夹或网络内容缓存等
Cache的内存容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时候,就需要挑选并舍弃相应的元素,从而腾出空间来存放新的内容。LRUCaChe的替换原则就是将最近最少使用的内容替换掉
2.LRUCache的实现
其实现方式可以有很多,但是为了追求最高的效率,我们采用哈希表和双向链表来实现LRUCaChe,哈希表的增删查改是O(1),双向链表可以实现任意位置插入删除为O(1)
2.1JDK中的LinkedHashMap

参数说明:
-
initialCapacity:容量大小 -
loadFacto:加载因子,使用无参构造方法时,此值默认为0.75f -
accessOrder:默认为false->基于插入顺序存放; true ->基于访问顺序
使用案例:
import java.util.LinkedHashMap;
import java.util.Map;public class LRUCache extends LinkedHashMap<Integer, Integer> {public int capacity;//容量public LRUCache(int capacity){super(capacity, 0.75f, true);//调用父类构造函数,必须放在构造函数第一行this.capacity = capacity;}public int get(int key){return super.getOrDefault(key, -1);}public void put(int key, int value){super.put(key, value);}//必须要重写上述方法@Overrideprotected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {return size() > capacity;//默认返回false,如果为true,则需要进行移除最近未使用的元素}
}
2.2LRUCaChe的实现
package LRUCache;import java.util.HashMap;
import java.util.Map;public class MyLRUCache {//双向链表节点static class LRUNode{public int val;public int key;public LRUNode prev;public LRUNode next;//给一个无参构造方法,初始化带头带尾双向链表头节点public LRUNode(){}public LRUNode(int key, int val){this.key = key;this.val = val;}//重写Object类中的方法,将双向链表节点值以字符串形式输出@Overridepublic String toString() {return "{" +"key=" + key +", val=" + val +'}';}}//需要声明一个哈希表,用来检查节点值是否存在private Map<Integer, LRUNode> cache;private int usedSize;//双向链表中有效数据的个数private int capacity;//双向链表的容量大小private LRUNode head, tail;public MyLRUCache(int capacity){this.usedSize = 0;this.capacity = capacity;cache = new HashMap<>();//实例化哈希表//伪头节点/尾节点head = new LRUNode();tail = new LRUNode();//先将链表头尾节点相连head.next = tail;tail.prev = head;}//存储元素public void put(int key, int val){//1.查找当前的key是否存储过LRUNode node = cache.get(key);//2.判断key在链表中是否存储过if (node != null){//存储过,更新节点对应的valnode.val = val;//将节点移动到末端moveToTail(node);}else{//没有存储过,新建一个节点值LRUNode cur = new LRUNode(key, val);//先插入哈希中cache.put(key, cur);//将节点添加到链表尾部addToTail(cur);usedSize++;//判断容量是否充足if(usedSize > capacity){//删除最近未使用的节点removeNode(head.next);//删除哈希表中的节点cache.remove(head.next.key);usedSize--;}}}private void addToTail(LRUNode node) {tail.prev.next = node;node.next = tail;node.prev = tail.prev;tail.prev = node;}private void moveToTail(LRUNode node) {//先删除节点removeNode(node);//将节点尾插addToTail(node);}//删除节点private void removeNode(LRUNode node) {node.prev.next = node.next;node.next.prev = node.prev;}//获取元素public int get(int key){//判断元素是否在链表中LRUNode node = cache.get(key);if(node == null) {return -1;}else{moveToTail(node);}return node.val;}public void printLRU(){LRUNode cur = head.next;while(cur != tail){System.out.print(cur);cur = cur.next;}System.out.println();}public static void main(String[] args) {MyLRUCache lruCache = new MyLRUCache(3);lruCache.put(100,10);lruCache.put(110,11);lruCache.put(120,12);lruCache.printLRU();System.out.println("获取元素");System.out.println(lruCache.get(110));System.out.println(lruCache.get(100));lruCache.printLRU();System.out.println("存放元素,会删除头节点,因为头节点是最近最少使用的: ");lruCache.put(999,99);lruCache.printLRU();}}
相关文章:
深入理解 并查集LRUCaChe
并查集&LRUCaChe 个人主页:顾漂亮 文章专栏:Java数据结构 1.并查集的原理 在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个单元素集合,然后根据一定规律将归于同一组元素的…...
最新版本SpringAI接入DeepSeek大模型,并集成Mybatis
当时集成这个环境依赖冲突,搞了好久,分享一下依赖配置 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instan…...
Effective Python:(17)
Effective Python提供90条python编程技巧和秘籍,对于我们养成良好的编程习惯,减少程序出错非常重要。 这条就是一条很好的建议,即尽量使用抛出异常来处理意外情况,尽量不要用none作为返回值进行判断。问题也比较显然,如…...
3-2 WPS JS宏 工作簿的打开与保存(模板批量另存为工作)学习笔记
************************************************************************************************************** 点击进入 -我要自学网-国内领先的专业视频教程学习网站 *******************************************************************************************…...
React + TypeScript 复杂布局开发实战
React TypeScript 复杂布局开发实战 一、项目架构设计(基于最新技术栈) 1.1 技术选型与工程创建 # 使用Vite 5.x React 19 TypeScript 5.4 npx create-vitelatest power-designer-ui --template react-ts cd power-designer-ui && npm inst…...
滑动验证组件-微信小程序
微信小程序-滑动验证组件,直接引用就可以了,效果如下: 组件参数: 1.enable-close:是否允许关闭,默认true 2.bind:onsuccess:验证后回调方法 引用方式: <verification wx:if&qu…...
Linux 命令大全完整版(12)
Linux 命令大全 5. 文件管理命令 ln(link) 功能说明:连接文件或目录。语 法:ln [-bdfinsv][-S <字尾备份字符串>][-V <备份方式>][--help][--version][源文件或目录][目标文件或目录] 或 ln [-bdfinsv][-S <字尾备份字符串>][-V…...
在VSCode中安装jupyter跑.ipynb格式文件
个人用vs用的较多,不习惯在浏览器单独打开jupyter,看着不舒服,直接上教程。 1、在你的环境中pip install ipykernel 2、在vscode的插件中安装jupyter扩展 3、安装扩展后,打开一个ipynb文件,并且在页面右上角配置内核 …...
IDEA配置JSP环境
首先下载IDEA2021.3,因为最新版本不能简单配置web开发环境。然后新建一个java开发项目: 然后右键创建的项目,添加web框架: 选择web appliciation 在web inf文件夹下创建classes和lib文件夹: 点击file ,选择…...
Idea 中 Project Structure简介
在 IntelliJ IDEA 中,Project Structure(项目结构)对话框是一个非常重要的配置界面,它允许你对项目的各个方面进行详细的设置和管理。下面将详细介绍 Project Structure 中各个主要部分的功能和用途。 1. Project(项…...
旁挂负载分担组网场景
旁挂负载分担组网场景(到路由策略) 1.拓扑 2.需求 使用传统三层架构中MSTPVRRP组网形式VLAN 2—>W3,SW4作为备份 VLAN 3—>SW4,SW3作为备份 MSTP设计—>SW3、4、5运行 实例1:VLAN 2 实例2:VLAN 3 3.配置 交换层 SW3配置 抢占延时ÿ…...
网络安全防御模型
目录 6.1 网络防御概述 一、网络防御的意义 二、被动防御技术和主动防御技术 三、网络安全 纵深防御体系 四、主要防御技术 6.2 防火墙基础 一、防火墙的基本概念 二、防火墙的位置 1.防火墙的物理位置 2.防火墙的逻辑位置 3. 防火墙的不足 三、防火墙技术类型 四…...
Qt 开源音视频框架模块之QtAV播放器实践
Qt 开源音视频框架模块QtAV播放器实践 1 摘要 QtAV是一个基于Qt的多媒体框架,旨在简化音视频播放和处理。它是一个跨平台的库,支持多种音视频格式,并提供了一个简单易用的API来集成音视频功能。QtAV的设计目标是为Qt应用程序提供强大的音视…...
MS SQL 2008 技术内幕:T-SQL 语言基础
《MS SQL 2008 技术内幕:T-SQL 语言基础》是一部全面介绍 Microsoft SQL Server 2008 中 T-SQL(Transact-SQL)语言的书籍。T-SQL 是 SQL Server 的扩展版本,增加了编程功能和数据库管理功能,使得开发者和数据库管理员能…...
【Pandas】pandas Series filter
Pandas2.2 Series Computations descriptive stats 方法描述Series.align(other[, join, axis, level, …])用于将两个 Series 对齐,使其具有相同的索引Series.case_when(caselist)用于根据条件列表对 Series 中的元素进行条件判断并返回相应的值Series.drop([lab…...
uake 网络安全 reverse网络安全
🍅 点击文末小卡片 ,免费获取网络安全全套资料,资料在手,涨薪更快 本文首发于“合天网安实验室” 首先从PEID的算法分析插件来介绍,要知道不管是在CTF竞赛的REVERSE题目中,还是在实际的商业产品中…...
vue实现根据点击或滑动展示对应高亮
页面需求: 点击左侧版本号,右侧展示对应版本内容并置于顶部右侧某一内容滚动到顶部时,左侧需要展示高亮 实现效果: 实现代码: <template><div><div class"historyBox pd-20 bg-white">…...
Magma:多模态 AI 智体的基础模型
25年2月来自微软研究、马里兰大学、Wisconsin大学、韩国 KAIST 和西雅图华盛顿大学的论文“Magma: A Foundation Model for Multimodal AI Agents”。 Magma 是一个基础模型,可在数字和物理世界中服务于多模态 AI 智体任务。Magma 是视觉-语言 (VL) 模型的重要扩展…...
浅显易懂HashMap的数据结构
HashMap 就像一个大仓库,里面有很多小柜子(数组),每个小柜子可以挂一串链条(链表),链条太长的时候会变成更高级的架子(红黑树)。下面用超简单的例子解释: 壹…...
怎么获取免费的 GPU 资源完成大语言模型(LLM)实验
怎么获取免费的 GPU 资源完成大语言模型(LLM)实验 目录 怎么获取免费的 GPU 资源完成大语言模型(LLM)实验在线平台类Google ColabKaggle NotebooksHugging Face Spaces百度飞桨 AI Studio在线平台类 Google Colab 特点:由 Google 提供的基于云端的 Jupyter 笔记本环境,提…...
Java SE与Java EE
Java SE(Java 平台标准版) Java SE 是 Java 平台的核心,提供了 Java 语言的基础功能。它包含了 Java 开发工具包(JDK),其中有 Java 编译器(javac)、Java 虚拟机(JVM&…...
02_linux系统命令
一、绝对路径与相对路径 1.以 ./ 开始的路径名是相对路径 2.以 / 开始的路径是绝对路径. 相对路径:会随着用户当前所在的目录发生改变. 绝对路径:不会根据用户所在的路径而改变. 3.gcc 编译器 编译器把高级语言(C语言/JAVA语言/C语言)生成二进制代码的一种工具.gcc 是专用…...
【leetcode hot 100 11】移动零
一、暴力解法:两个 for 循环,外层循环遍历所有可能的左边界,内层循环遍历所有可能的右边界 class Solution {public int maxArea(int[] height) {int max_area0;for(int i0; i<height.length; i){for(int ji1; j<height.length; j){in…...
AI绘画软件Stable Diffusion详解教程(2):Windows系统本地化部署操作方法(专业版)
一、事前准备 1、一台配置不错的电脑,英伟达显卡,20系列起步,建议显存6G起步,安装win10或以上版本,我的显卡是40系列,16G显存,所以跑大部分的模型都比较快; 2、科学上网࿰…...
轨迹控制--odrive的位置控制---负载设置
轨迹控制 此模式使您可以平滑地使电机旋转,从一个位置加速,匀速和减速到另一位置。 使用位置控制时,控制器只是试图尽可能快地到达设定点。 使用轨迹控制模式可以使您更灵活地调整反馈增益,以消除干扰,同时保持平稳的运…...
【安卓逆向】逆向APP界面UI修改再安装
1.背景 有一客户找到我,说能不能把APP首页的底部多余界面去掉。 逆向实战 想要去除安卓应用软件中的内容,需要对APP逆向进行修改再打包。 通过工具 MIT管理器工具 提取APK包,点击apk文件,点击查看反编译apk。 搜索关键字。这里关键…...
SAP Webide系列(7)- 优化FreeStyle新建项目预设模板
目录 一、背景 二、优化目标 三、定位调整点 四、调整步骤 五、效果展示 六、附言 一、背景 在每次通过Webide进行FreeStyle方式自开发SAP UI5应用的时候,新建项目,得到的模板文件都是只有很少的内容(没有路由配置、没有设置默认全屏等…...
python读取sqlite温度数据,并画出折线图
需求: 在Windows下请用python画出折线图,x轴是时间,y轴是温度temperature 和体感温度feels_like_temperature 。可以选择县市近1小时,近1天,近1个月的。sqlite文件weather_data.db当前目录下,建表结构如下…...
URL 对字母大小写敏感么?
URL 的不同部分对大小写的敏感性不同: 协议部分: 不区分大小写 例如:http:// 和 HTTP:// 被视为相同域名部分(主机名): 不区分大小写 例如:example.com 和 ExAmPle.CoM 被视为相同路径部分 例如:example.c…...
【Linux网络编程】高效I/O--select/poll服务器
目录 多路转接之select select服务器实现 获取连接 handlerEvent select服务器代码链接 select的优缺点 多路转接之poll poll服务器实现(select服务器改写) poll的优缺点 多路转接之select select的作用 I/O的本质 等 拷贝 多路转接就是通过同时等待多个文件描述…...
