并查集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.mzph.cn/news/92596.shtml

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

相关文章

[FineReport]安装与使用(连接Hive3.1.2)

一、安装(对应hive3.1.2) 注&#xff1a;服务器的和本地的要同时安装。本地是测试环境&#xff0c;服务器的是生产环境 1、服务器安装 1、下载 免费下载FineReport - FineReport报表官网 向下滑找到 2、解压 [rootck1 /home/data_warehouse/software]# tar -zxvf tomcat…

数据挖掘(1)概述

一、数据仓库和数据挖掘概述 1.1 数据仓库的产生 数据仓库与数据挖掘&#xff1a; 数据仓库和联机分析处理技术(存储)。数据挖掘&#xff1a;在大量的数据中心挖掘感兴趣的知识、规则、规律、模式、约束(分析)。数据仓库用于决策分析&#xff1a; 数据仓库&#xff1a;是在数…

机器学习算法基础--K-means应用实战--图像分割

目录 1.项目内容介绍 2.项目关键代码 3.项目效果展示 1.项目内容介绍 本项目是将一张图片进行k-means分类&#xff0c;根据色彩k进行分类&#xff0c;最后比较和原图的效果。 题目还是比较简单的&#xff0c;我们只要通过k-means聚类&#xff0c;一类就是一种色彩得出聚类之…

快速上手kettle(三)壶中可以放些啥?

序言 快速上手kettle开篇中,我们将kettle比作壶,并对这个壶做了简单介绍。 而上一期中我们实现了①将csv文件通过kettle转换成excel文件; ②将excel文件通过kettle写入到MySQL数据库表中 这两个案例。 相信大家跟我一样,对kettle已经有了初步认识,并且对这强大的工具产…

CV面试知识点总结

一.卷积操作和图像处理中的中值滤波操作有什么区别&#xff1f; 1.1卷积操作 卷积操作是一种线性操作&#xff0c;通常用于特征的提取&#xff0c;通过卷积核的加权求和来得到新的像素值。1.2中值滤波 原文&#xff1a; https://blog.csdn.net/weixin_51571728/article/detai…

leetCode 376.摆动序列 动态规划 + 图解 + 状态转移

376. 摆动序列 - 力扣&#xff08;LeetCode&#xff09; 如果连续数字之间的差严格地在正数和负数之间交替&#xff0c;则数字序列称为 摆动序列 。第一个差&#xff08;如果存在的话&#xff09;可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。 例如…

[尚硅谷React笔记]——第2章 React面向组件编程

目录&#xff1a; 基本理解和使用&#xff1a; 使用React开发者工具调试函数式组件复习类的基本知识类式组件组件三大核心属性1: state 复习类中方法this指向&#xff1a; 复习bind函数&#xff1a;解决changeWeather中this指向问题&#xff1a;一般写法&#xff1a;state.htm…

【最新版配置conda环境】新版pycharm导入新版anaconda环境

最近下载了新版pycharm和新版anaconda&#xff0c;并且在命令行创建了环境&#xff0c;想着在pycharm里面导入环境。结果现在的导入方式发生了变化。 之前是通过导入Python.exe进行的。 现在&#xff1a; 当我们点击进去之后&#xff0c;会发现找不到python.exe了。 具体什么…

JVM学习笔记

JVM学习笔记 复习之前学的内容&#xff0c;同时补充以下知识点&#xff1a;JVM的双亲委派机制、伊甸区与老年代相关知识&#xff1b; 双亲委派机制 双亲的含义应该就是AppClassLoader有&#xff1a;ExtClassLoader和BootstrapClassLoader“两个”父加载器。 首先介绍Java中…

Stm32_标准库_4_TIM中断_PWM波形_呼吸灯

基本原理 PWM相关物理量的求法 呼吸灯代码 #include "stm32f10x.h" // Device header #include "Delay.h"TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStructuer;//结构体 GPIO_InitTypeDef GPIO_InitStructur…

【Git】Git 原理和使用

Git 一、Git 本地仓库1. 本地仓库的创建2. 配置 Git3. 工作区、暂存区、版本库4. 添加文件5. 查看 .git 文件6. 修改文件7. 版本回退8. 撤销修改9. 删除文件 二、分支管理1. 理解分支2. 创建分支3. 切换分支4. 合并分支5. 删除分支6. 合并冲突7. 分支管理策略8. bug 分支9. 强制…

TempleteMethod

TempleteMethod 动机 在软件构建过程中&#xff0c;对于某一项任务&#xff0c;它常常有稳定的整体操作结构&#xff0c;但各个子步骤却有很多改变的需求&#xff0c;或者由于固有的原因 &#xff08;比如框架与应用之间的关系&#xff09;而无法和任务的整体结构同时实现。如…

Armv8/Armv9 Cache知识大纲分享--思维导图

关键词&#xff1a;cache学习、mmu学习、cache资料、mmu资料、arm资料、armv8资料、armv9资料、 trustzone视频、tee视频、ATF视频、secureboot视频、安全启动视频、selinux视频&#xff0c;cache视频、mmu视频&#xff0c;armv8视频、armv9视频、FF-A视频、密码学视频、RME/CC…

Acwing 838. 堆排序

Acwing 838. 堆排序 题目描述思路讲解代码展示 题目描述 思路讲解 堆是一颗完全二叉树&#xff0c;除了最下面一层&#xff0c;其余是满的&#xff0c;最后一层从左到右排列 小根堆&#xff1a;每个点小于等于左右两堆&#xff0c;所以根节点就是最小值 大根堆&#xff1a;每个…

Docker Tutorial

什么是Docker 为每个应用提供完全隔离的运行环境 Dockerfile&#xff0c; Image&#xff0c;Container Image&#xff1a; 相当于虚拟机的快照&#xff08;snapshot&#xff09;里面包含了我们需要部署的应用程序以及替它所关联的所有库。通过image&#xff0c;我们可以创建很…

美容店预约小程序搭建流程

随着科技的不断发展&#xff0c;小程序已经成为了人们生活中不可或缺的一部分。对于美容店来说&#xff0c;搭建一个预约小程序不仅可以提高工作效率&#xff0c;还可以增加客户数量、提高服务质量。那么&#xff0c;如何搭建一个美容店预约小程序呢&#xff1f;本文将为你详细…

git使用,一点点

查看自己有没有安装git git --version 如果没有安装请执行sudo yum install -y git来安装 git 指令 git log 查看日志 git pull 同步远端和本地仓库 这就是冲突的报错&#xff1a; 所以这个时候你要同步一下git pull

【项目实战】单数据源多数据库实现多租户

文章目录 前言多租户的四种实现方案单数据源多数据库实现思路代码实现 总结 前言 多租户&#xff08;Multi-Tenancy&#xff09;是一种软件架构设计模式&#xff0c;旨在使单个应用程序可以同时为多个租户&#xff08;如不同组织、用户或客户&#xff09;提供服务&#xff0c;…

CSS 相关

CSS 相关 CSS布局如何管理CSS 代码目录&#xff1f;分多个目录的话&#xff0c;会有命名冲突&#xff0c;那如何解决命名冲突&#xff1f; box-sizing:border-boximage的宽度的问题&#xff1a; CSS布局 单列布局&#xff1a;将一个元素作为布局容器。通常设置一个较小的宽度(最…

MATLAB中d2d函数用法

目录 语法 说明 示例 重新采样离散时间模型 重新采样已识别的离散时间模型 d2d函数的功能是重新采样离散时间模型。 语法 sys1 d2d(sys, Ts) sys1 d2d(sys, Ts, method) sys1 d2d(sys, Ts, opts) 说明 sys1 d2d(sys, Ts)将离散时间动态系统模型 sys 重新采样&#…