并查集LRUCache

文章目录

  • 并查集
    • 1.概念
    • 2. 实现
  • LRUCache
    • 1. 概念
    • 2. 实现
      • 使用标准库实现
      • 自主实现


并查集

1.概念

并查集是一个类似于森林的数据结构,并、查、集指的是多个不相干的集合直接的合并和查找,并查集使用于N个集合。适用于将多个元素分成多个集合,在分成集合的过程中要反复查询某两个元素是否存在于同一个集合的场景

比如说:总共有10个人要去执行任务,他们想进行组队,他们的编号从0到9,入下图:假设-1表示队伍里存在一人。

在这里插入图片描述

他们组成了3个队伍, 0 , 6 , 7 , 8 {0,6,7,8} 0,6,7,8 一队, 0 0 0是队长,组队这个操作其实就是进行了合并。

在这里插入图片描述

上图的关系可以用数组来表示:

  1. 数组的下标对应集合中元素的编号
  2. 数组中如果为负数,负号代表根,数字代表该集合中元素个数
  3. 数组中如果为非负数,代表该元素双亲在数组中的下标

从图可以看出,0下标就是一个根节点(队长),他有三个子节点(队员),同样的下标1和小标2都是一个根节点,它们都有两个子节点。

在这里插入图片描述

2. 实现

/*** 并查集*/
public class UnionFind {private int[] elem;public UnionFind(int size) {this.elem = new int[size];Arrays.fill(elem,-1);}/*** 查找x的根* @param x* @return*/public int findRoot(int x) {if (elem[x] == -1) {return x;}while (elem[x] >= 0) {x = elem[x];}return x;}/*** 判断两个元素是否在同一个集合* @param x* @param y* @return*/public boolean isSameSet(int x, int y) {int xRoot = findRoot(x);int yRoot = findRoot(y);if (xRoot == yRoot) {return true;}return false;}/*** 合并x和y,x和y必须是根节点,先查找根* @param x* @param y* @return*/public void union(int x,int y) {int xRoot = findRoot(x);int yRoot = findRoot(y);if (xRoot ==  yRoot) {// 在同一个集合的元素无需合并return;}elem[xRoot] += elem[yRoot];elem[yRoot] = xRoot;}/*** 获取并查集中有多少集合* @return*/public int getCount() {int count = 0;for (int tmp : elem) {if (tmp < 0) {count++;}}return count;}}

LRUCache

1. 概念

LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法 ,LRUCache其实就是淘汰最近最少使用的缓存的一种数据结构,Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时, 就需要挑选并舍弃原有的部分内容,从而腾出空间来放新内容。LRU Cache 的替换原则就是将最近最少使用的内容替换掉

2. 实现

实现高效的LRUCache可以使用哈希表+双向链表来实现。哈希表的增删改查都是 O ( 1 ) O(1) O(1),而双向链表的任意位置的删除也是 O ( 1 ) O(1) O(1)

JDK中类似LRUCahe的数据结构LinkedHashMap

LinkedHashMap和HashMap有点类似,LinkedHashMap也是使用数组+链表实现,但LinkedHashMap使用的是双向链表且维护了一个头节点和尾巴节点,同时保证了插入顺序,也可以动态调整元素顺序。

LinkedHashMap的构造方法参数:

  • initialCapacity:初始容量大小,使用无参构造方法时,此值默认是16
  • loadFactor:加载因子,使用无参构造方法时,此值默认是 0.75f
  • access Order:false: 基于插入顺序 true: 基于访问顺序

如果accessOrder为false表示按照插入顺序进行排序,如果为true表示按照访问顺序来排序,假设访问元素全部插入后,访问了1获取了张三,那么张三就会被放到末尾去,反之设置为false则不会进行调整。

public static void main(String[] args) {Map<Integer,String> map = new LinkedHashMap<>(16,0.75f,false);map.put(1,"张三");map.put(2,"李四");map.put(3,"王五");map.put(4,"张六");map.put(5,"李七");System.out.println(map);map.get(2);map.get(1);map.put(4,"里");System.out.println(map);}

运行结果

{1=张三, 2=李四, 3=王五, 4=张六, 5=李七}
调用后
{1=张三, 2=李四, 3=王五, 4=, 5=李七}

假设把LinkedHashMap的参数改为true

public static void main(String[] args) {Map<Integer,String> map = new LinkedHashMap<>(16,0.75f,true);map.put(1,"张三");map.put(2,"李四");map.put(3,"王五");map.put(4,"张六");map.put(5,"李七");System.out.println(map);System.out.println("调用后");map.get(2);map.get(1);map.put(4,"里");System.out.println(map);}

运行结果

{1=张三, 2=李四, 3=王五, 4=张六, 5=李七}
调用后
{3=王五, 5=李七, 2=李四, 1=张三, 4=}

使用标准库实现

继承LinkedHashMap重写关键方法removeEldestEntry即可实现LRUCache,removeEldestEntry方法默认返回false,返回false表示LinkedHashMap没有容量限制,如果返回true表示如果元素超过了capacity就会抛弃最旧元素,最旧元素可能是最先插入的元素,也可能是插入后一直没有使用的元素也是最旧元素。

public class MyLRUCache extends LinkedHashMap<Integer,Integer> {private int capacity;/***super(容量,负载因子,基于插入顺序/基于访问顺序);* @param capacity*/public MyLRUCache(int capacity) {super(capacity,0.75f,true);this.capacity = capacity;}public int get(int key) {Integer ret = super.get(key);if (ret == null) {return -1;}return super.get(key);}/*** 重写关键方法,默认放回false,重写后如LRU中元素大于指定容量capacity就会删除最旧元素* @param eldest* @return*/@Overrideprotected boolean removeEldestEntry(Map.Entry eldest) {return size() > capacity;}public void put(int key, int value) {super.put(key, value);}public static void main1(String[] args) {Map<Integer,String> map = new LinkedHashMap<>(16,0.75f,true);map.put(1,"张三");map.put(2,"李四");map.put(3,"王五");map.put(4,"张六");map.put(5,"李七");System.out.println(map);System.out.println("调用后");map.get(2);map.get(1);map.put(4,"里");System.out.println(map);}public static void main(String[] args) {MyLRUCache lruCache = new MyLRUCache(4);lruCache.put(1,0);lruCache.put(2,0);lruCache.put(3,0);lruCache.put(4,0);lruCache.put(5,0);System.out.println(lruCache.get(1));System.out.println(lruCache);}}

自主实现

自主实现一个LRU使用标准库中的HashMap和手写的带头尾傀儡节点的双向链表来实现。

  • HashMap的key存放的是存入的键值key,value是一个双向链表的节点
  • 双向链表节点属性有存放的键(key)和值(value),前驱节点和后继节点
  • LRU中有两个傀儡节点保证插入的顺序,每次插入元素都插入到tail节点前面
  • 每次元素达到最大缓存容量时,默认丢失head节点的后一个元素
  • 没一次get或者put,也就是获取或者更新元素,都把该元素移动到末尾,也就是tail前面

在这里插入图片描述

使用元素移动

在这里插入图片描述

public class LRUCache {static class Node {int key;int val;Node prev;Node next;public Node() {}public Node(int key,int val) {this.key = key;this.val = val;}@Overridepublic String toString() {return "Node{" +"key=" + key +", val=" + val +'}';}}// 最大容量private int capacity;// 头尾傀儡节点private Node head;private Node tail;private Map<Integer,Node> map;// 链表有效元素个数private int size;public LRUCache(int capacity) {this.capacity = capacity;this.map = new HashMap<>();this.head = new Node();this.tail = new Node();this.head.next = tail;this.tail.prev = head;}/*** 从LRUCache中获取元素* @param key* @return*/public int get(int key) {Node node = map.get(key);if (node != null) {// 把node放到链表尾部moveNodeIsTail(node);return node.val;}// 不存在返回-1return -1;}/*** 将node节点移动到链表尾部* @param node*/private void moveNodeIsTail(Node node) {node.prev.next = node.next;node.next.prev = node.prev;tail.prev.next = node;node.prev = tail.prev;node.next = tail;tail.prev = node;}/*** 添加元素到LRUCache中* @param key* @param val*/public void put(int key,int val) {Node tmp = map.get(key);if (tmp == null) {// 不存在就创建节点添加到尾部Node node = new Node(key,val);map.put(key,node);tail.prev.next = node;node.prev = tail.prev;node.next = tail;tail.prev = node;this.size++;// 判断是否超过容量capacityif (this.size > capacity) {// 丢弃链表第一个元素(长时间未使用元素)Node cur = head.next;cur.next.prev = head;head.next = cur.next;map.remove(cur.key);}} else {// 存在就直接更新值并放到尾部tmp.val = val;moveNodeIsTail(tmp);}}public void print() {Node cur = head.next;while (cur != tail) {System.out.print(cur+" ");cur = cur.next;}System.out.println();}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

脉冲法和方向盘转角法计算车辆位置不同应用工况

1. 脉冲法计算车辆位置 在定义下的世界坐标系中&#xff0c;车辆运动分为右转后退、右转前进、左转后退、左转前进、直线前进、直线后退和静止七种工况&#xff0c;因此需要推倒出一组包含脉冲、车辆运动方向和车辆结构尺寸参数的综合方程式进行车辆轨迹的实时迭代计算。由于直…

Linux:nginx---web文件服务器

我这里使用的是centos7系统 nginx源码包安装 Linux&#xff1a;nginx基础搭建&#xff08;源码包&#xff09;_鲍海超-GNUBHCkalitarro的博客-CSDN博客https://blog.csdn.net/w14768855/article/details/131445878?ops_request_misc%257B%2522request%255Fid%2522%253A%25221…

【AntDesign】封装全局异常处理-全局拦截器

[toc] 场景 本文前端用的是阿里的Ant-Design框架&#xff0c;其他框架也有全局拦截器&#xff0c;思路是相同&#xff0c;具体实现自行百度下吧 因为每次都需要调接口&#xff0c;都需要单独处理异常情况&#xff08;code !0&#xff09;&#xff0c;因此前端需要对后端返回的…

每日一博 - 闲聊 Java 中的中断

文章目录 概述常见的中断问题中断一个处于运行状态的线程中断一个正在 sleep 的线程中断一个由于获取 ReentrantLock 锁而被阻塞的线程 如何正确地使用线程的中断标识JDK 的线程池 ThreadPoolExecutor 内部是如何运用中断实现功能的小结 概述 在 Java 中&#xff0c;中断是一种…

应用在手机触摸屏中的电容式触摸芯片

触控屏&#xff08;Touch panel&#xff09;又称为触控面板&#xff0c;是个可接收触头等输入讯号的感应式液晶显示装置&#xff0c;当接触了屏幕上的图形按钮时&#xff0c;屏幕上的触觉反馈系统可根据预先编程的程式驱动各种连结装置&#xff0c;可用以取代机械式的按钮面板&…

ElementUI实现登录注册啊,axios全局配置,CORS跨域

一&#xff0c;项目搭建 认识ElementUI ElementUI是一个基于Vue.js 2.0的桌面端组件库&#xff0c;它提供了一套丰富的UI组件&#xff0c;包括表格、表单、弹框、按钮、菜单等常用组件&#xff0c;具备易用、美观、高效、灵活等优势&#xff0c;能够极大的提高Web应用的开发效…

Lua函数

--函数--无参无返回值 function F1()print("F1函数") end F1() print("*****************")--有参 function F2(a)print("F2函数"..a) end F2(2) --如果传入参数和函数数量不一致 --不会报错只是补空 F2(1,2) print("*****************&quo…

【夏虫语冰】测试服务器端口是否打开(命令行、Python)

文章目录 1、简介2、命令行2.1 telnet2.1.1 工具简介2.1.2 工具配置2.1.3 工具使用 2.2 curl2.2.1 工具简介2.2.1 工具下载2.2.1 工具使用 2.3 wget2.3.1 工具简介2.3.2 工具下载2.3.2 工具使用 2.4 nc2.4.1 工具简介2.4.2 工具安装2.4.3 工具使用 2.5 ssh2.5.1 工具简介2.5.2 …

数据链路层 MTU 对 IP 协议的影响

在介绍主要内容之前&#xff0c;我们先来了解一下数据链路层中的"以太网" 。 “以太网”不是一种具体的网络&#xff0c;而是一种技术标准&#xff1b;既包含了数据链路层的内容&#xff0c;也包含了一些物理层的内容。 下面我们再来了解一下以太网数据帧&#xff…

[Machine learning][Part3] numpy 矢量矩阵操作的基础知识

很久不接触数学了&#xff0c;machine learning需要用到一些数学知识&#xff0c;这里在重温一下相关的数学基础知识 矢量 矢量是有序的数字数组。在表示法中&#xff0c;矢量用小写粗体字母表示。矢量的元素都是相同的类型。例如&#xff0c;矢量不包含字符和数字。数组中元…

Android Jetpack组件架构:ViewModel的原理

Android Jetpack组件架构&#xff1a;ViewModel的原理 导言 本篇文章是关于介绍ViewModel的&#xff0c;由于ViewModel的使用还是挺简单的&#xff0c;这里就不再介绍其的基本应用&#xff0c;我们主要来分析ViewModel的原理。 ViewModel的生命周期 众所周知&#xff0c;一般…

字节一面:深拷贝浅拷贝的区别?如何实现一个深拷贝?

前言 最近博主在字节面试中遇到这样一个面试题&#xff0c;这个问题也是前端面试的高频问题&#xff0c;我们经常需要对后端返回的数据进行处理才能渲染到页面上&#xff0c;一般我们会讲数据进行拷贝&#xff0c;在副本对象里进行处理&#xff0c;以免玷污原始数据&#xff0c…

力扣 -- 10. 正则表达式匹配

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:bool isMatch(string s, string p) {int ms.size();int np.size();//处理后续映射关系s s;//处理后续映射关系p p;vector<vector<bool>> dp(m1,vector<bool>(n1));//初始化dp[0][0]true…

支付宝支付模块开发

生成二维码 使用Hutool工具类生成二维码 引入对应的依赖 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.5</version> </dependency><dependency><groupId>com.go…

公司知识库搭建步骤,知识库建设与运营的四个步骤分享

在知识管理方面&#xff0c;团队中的每一员&#xff0c;都像是一名独行侠&#xff0c;自己的知识&#xff0c;满足自己的需要&#xff0c;这其中&#xff0c;就造成了很多无意义的精力消耗。 公司知识库搭建必要性 比如&#xff0c;一名员工撰写一QA文档&#xff0c;并没有将它…

国庆《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书行将售罄

国庆《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书行将售罄 国庆《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书行将售罄

【CUDA编程概念】一、什么是bank conflict?

前言 搜了不少答案&#xff0c;大多是在避免Bank Conflict&#xff0c;很难找到一个关于Bank Conflict的详细定义&#xff0c;这里找了些资料来尝试解释下&#xff1b; 一、基础概念 先简单复习下相关概念 GPU调度执行流程&#xff1a; SM调度单位为一个warp&#xff08;一…

Linux:修改mvn命令使用的maven路径

要在 Linux 上更改 Maven 的版本&#xff0c;需要调整 PATH 环境变量以指向所需版本的 Maven 安装目录。 打开终端或命令行界面。 使用文本编辑器打开 /etc/profile 文件&#xff1a; vi /etc/profile在文件的末尾添加以下行&#xff0c;将 PATH 环境变量指向新的 Maven 安装目…

4项简化IT服务台任务的ChatGPT功能

近几个月&#xff0c;随着人工智能聊天机器人 ChatGPT 风靡全球&#xff0c;用户可以通过它生成脚本、文章、运动计划表等。同时&#xff0c;这项技术在各行各业都能够进行无穷无尽的应用&#xff0c;在本文中&#xff0c;我们将探讨这项现代技术如何帮助ITSM团队提升服务交付和…

面试题六:Promise的使用,一文详细讲解

含义 Promise是异步编程的一种解决方案&#xff0c;比传统的解决方案&#xff08;回调函数和事件&#xff09;更合理更强大。 所谓Promise&#xff0c;简单说就是一个容器&#xff0c;里面保存着某个未来才会结束的事件 (通常是一个异步操作)的结果。从语法上说&#xff0c;P…