并查集 / LRUCache

并查集

概念

在一些应用问题中,将N个不同元素划分成一些不相交的集合 ,开始时每个元素自成一个集合 ,然后按照一定规律将同一组元素进行合并。为了方便查询不同元素是否为一个集合 (是否都与某个数据有关联 ,通常一个集合中,每个数据统一都与一个数据有关联,这个数据称为),引入了并查集这一数据结构

原则

1. 数组下标 对应集合中元素的编号

2. 下标对应的元素大于0:表示该编号所在集合的根

3 .下标对应的值小于零:负号代表这个下标表示根 ,数字代表这个集合中有多少个元素

应用

并查集一般用于解决以下问题:

1.查找元素属于哪个集合/是否为同一集合

沿着数组一直找到根 (负数的位置),对应的下标表示这个集合中的根,若下标相同,则代表是同一集合

2.将两个集合合并为一个集合

A集合根下标对应的元素B集合根下标对应的元素相加,并存储到A集合的根元素,然后将B集合的根元素改为A集合的根下标

3.集合的个数

遍历数组,计算负数元素的个数

简单实现

因为初始情况下,数组中每个元素都代表着一个集合,集合中元素的个数都为1,所以将数组每个元素都初始化为-1

构造方法

java 复制代码
    public int[] elem;
    public MyUnionFindSet(int val) {
        this.elem = new int[val];
        Arrays.fill(elem,-1);
    }

检查两个元素是否是同一集合

首先分别找到两个元素根 ,并判断根是否一样

java 复制代码
    private int findRoot(int x){
        if(x<0) throw new ArrayIndexOutOfBoundsException();
        while(elem[x]>=0){
            x=elem[x];
        }
        return x;
    }
    public boolean isSameSet(int x1,int x2){
        if(x1==x2) return true;
        int r1=findRoot(x1);
        int r2=findRoot(x2);
        return r1==r2;
    }

将两个元素所在的集合合并

首先判断这两个元素是否在同一集合,如果在同一集合,那么不需要合并

如果不在同一集合,修改集合A和集合B根下标对应的元素

java 复制代码
    public void union(int x1,int x2){
        if(x1==x2) return;
        int r1=findRoot(x1);
        int r2=findRoot(x2);
        if(r1==r2) return;
        elem[r1]=elem[r1]+elem[r2];
        elem[r2]=r1;
    }

计算集合的个数

计算数组中负数元素的个数

java 复制代码
    public int getCount(){
        int count=0;
        for(int x:elem ){
            if(x<0) count++;
        }
        return count;
    }

有关并查集的oj题

省份数量

依次遍历二维数组的每一行每一列,如果元素为1,那么将两个下标按照并查集规则合并

java 复制代码
class Solution {
    public static int[] elem;
    private int findRoot(int x){
        while(elem[x]>=0){
            x=elem[x];
        }
        return x;
    }
     private void union(int x,int y){
        if(x==y) return;
        int r1=findRoot(x);
        int r2=findRoot(y);
        if(r1==r2) return;
        elem[r1]=elem[r1]+elem[r2];
        elem[r2]=r1;
     }
    public int findCircleNum(int[][] isConnected) {
        int len=isConnected[0].length;
        elem=new int[len];
        for(int i=0;i<len;i++){
            elem[i]=-1;
        }
        for(int i=0;i<len;i++){
            for(int j=0;j<len;j++){
                if(isConnected[i][j]==1) union(i,j);
            }
        }
        int count=0;
        for(int x:elem){
            if(x<0) count++;
        }
        return count;
    }
}

等式方程的可满足性

首先判断等号的情况:只要是等号,那么就按照并查集的原则进行合并

然后再次遍历,判断不等号的情况:如果是不等号,那么找到两个元素的根 ,如果根相同 ,那么返回false

两次遍历之后如果没有返回false那么最后返回true

java 复制代码
class Solution {
    public static int[] elem;
    private int findRoot(int x){
        while(elem[x]>=0){
            x=elem[x];
        }
        return x;
    }
    private boolean union(int x,int y){
        if(x==y) return true;
        int r1=findRoot(x);
        int r2=findRoot(y);
        if(r1==r2) return true;
        elem[r1]=elem[r1]+elem[r2];
        elem[r2]=r1;
        return true;
    }
    public boolean equationsPossible(String[] equations) {
        elem=new int[27];
        for(int i=0;i<27;i++){
            elem[i]=-1;
        }
        for(String str:equations){
            if(str.charAt(1)=='='){
                union(str.charAt(0)-'a',str.charAt(3)-'a');
            }
        }
        for(String str:equations){
            if(str.charAt(1)=='!'){
                int r1=findRoot(str.charAt(0)-'a');
                int r2=findRoot(str.charAt(3)-'a');
                if(r1==r2) return false;
            }
        }
        return true;
    }
}

LRUCache

LRUCache是Least Recently Used 的缩写,意思是最近最少使用(很久没使用) 。Cache在广义上指的是位于速度相差较大的两个硬件之间,用于协调两者数据传输速度差异的结构

Cache的容量有限,因此当Cache的容量用完之后,又有新的元素想要插入进来,那么就要舍弃原有的部分内容。LRUCache替换原则就是将最近最少使用的内容替换掉 ,就是很久没使用的元素

为了实现快速的查找与删除,LRUCache一般使用哈希表+双向链表来实现

LinkedHashMap

LinkedHashMap 是JDK中类似LRUCache的数据结构

构造方法:

initialCapacity :初始容量

loadFactor 负载因子

accessOrder false:按照插入顺序排序

true:按照访问时间,动态排序 ,最近一次访问过的数据放在最后

通过哈希表和双向链表简单实现

首先定义一个静态内部类 用来实例化双向链表的节点 ,为了方便插入和删除,我们在头和尾都引入傀儡节点,所以在声明节点的内部类中,要给一个没有参数的构造方法和传 key , value 的构造方法

java 复制代码
    static class LinkedNode{
        int val;
        int key;
        LinkedNode next;
        LinkedNode prev;
        public LinkedNode(int key,int val) {
            this.val = val;
            this.key=key;
        }
        public LinkedNode() {

        }
    }

我们使用库里的HashMap来实现哈希表,用来存放key和对应的双向链表节点

构造方法:

java 复制代码
    public MyLRUCache(int capacity) {
        cache=new HashMap<>();
        head=new LinkedNode();
        tail=new LinkedNode();
        this.capacity=capacity;
        usedSize=0;
    }

添加元素(put)

首先通过哈希表来检测要添加的元素key是否存在

java 复制代码
LinkedNode node=cache.get(key);
已经存在

1.更新node节点上的val值

2.移除node节点

3.将node节点添加到尾部

java 复制代码
    private void removeHead(LinkedNode node){
        node.prev.next=node.next;
        node.next.prev=node.prev;
    }
    private void addToTail(LinkedNode node){
        if(usedSize==0){
            head.next=node;
            node.prev=head;
            node.next=tail;
            tail.prev=node;
        }else{
            tail.prev.next=node;
            node.prev=tail.prev;
            node.next=tail;
            tail.prev=node;
        }
    }
     //put方法:
       node.val=val;
       removeHead(node);
       addToTail(node);
不存在

1.定义一个node节点存放key和value

2.将key和node存放到哈希表

3.将node添加到链表的尾部并且usedSize++

4.如果usedSize大于 设定的容量,那么移除头节点 ,并且在哈希表中删除对应的key,同时usedSize--

java 复制代码
            LinkedNode nodeN=new LinkedNode(key,val);
            cache.put(key,nodeN);
            addToTail(nodeN);
            this.usedSize++;
            if(usedSize>capacity){
                removeHead(head.next);
                cache.remove(head.next.key);
                this.usedSize--;
            }

获取元素(get)

通过哈希表的get方法来获取对应的node

如果node==null则不存在

如果node!=null 则返回对应的val值 ,并且将node节点移到尾部

java 复制代码
    public int get(int key){
        LinkedNode node=cache.get(key);
        if(node==null){
            return -1;
        }
        removeHead(node);
        addToTail(node);
        return node.val;
    }
相关推荐
大千AI助手41 分钟前
高维空间中的高效导航者:球树(Ball Tree)算法深度解析
人工智能·算法·机器学习·数据挖掘·大千ai助手·球树·ball-tree
豐儀麟阁贵42 分钟前
9.2连接字符串
java·开发语言·算法
苏小瀚1 小时前
[算法]---斐波那契数列模型
算法
严文文-Chris2 小时前
【监督学习常用算法总结】
学习·算法
feifeigo1232 小时前
电池的荷电状态(SOC)估计
算法
博语小屋3 小时前
力扣 15.三数之和(medium)(双指针)
算法·leetcode·职场和发展
无敌最俊朗@3 小时前
双指针-力扣hot100-移动零.283
算法·leetcode·职场和发展
练习时长一年3 小时前
LeetCode热题100(腐烂的橘子)
算法·leetcode·职场和发展
Тиё Сиротака8 小时前
红包分配算法的严格数学理论与完整实现
算法