首页 > 编程语言 >LFU缓存算法(理解容易,主要是代码实现内外双map+双双向链表)

LFU缓存算法(理解容易,主要是代码实现内外双map+双双向链表)

时间:2023-09-07 22:34:59浏览次数:37  
标签:node map headList NodeList LFU 链表 节点 public Node

package arithmetic;

import java.util.HashMap;

public class FaceTest82 {
//LFU缓存置换算法
//比较词频,词频相同看时间点
//置换之后,词频重新开始累计
public FaceTest82(int k) {
capacity = k;
size = 0;
records = new HashMap<Integer, FaceTest82.Node>();
heads = new HashMap<>();
headList = null;
}

private int capacity;//最大缓存大小
private int size;//目前的节点数
private HashMap<Integer, Node> records;//表示key(Integer)由哪个节点代表
private HashMap<Node,NodeList> heads;//表示节点(Node)在哪个桶(NodeList) 里面
private NodeList headList;//整个结构中位于最左的桶 头桶

//节点的数据结构
public static class Node{
	public Integer key;
	public Integer value;
	public Integer times;//节点操作次数总和
	public Node up; //节点之间是双向链表所以有上一个节点
	public Node down; //节点之间是双向链表所以有下一个节点
	public Node(int k,int v,int t) {
		key = k;
		value = v;
		times = t;
	}
}

//桶结构
public static class NodeList {
	public Node head;//桶的头结点
	public Node tail;//桶的尾结点
	public NodeList last;//桶之间是双向链表所以有前一个
	public NodeList next;//桶之间是双向链表所以有后一个
	public NodeList(Node node) {
		head = node;
		tail = node;
	}
	//把一个新节点加入这个桶,新的节点都放在顶端变成新头结点
	public void addNodeFromHead(Node newHead) {
		newHead.down = head;
		head.up = newHead;
		head = newHead;
	}
	
	//判断这个桶是不是空的桶
	public boolean isEmpty() {
		return head == null;
	}
	
	//删除节点并保证node的上下文环境重新连接
	public void deleteNode(Node node) {
		if (head == tail) {
			head = null;
			tail = null;
		} else {
			//先保证上下节点重新连接
			if (node == head) {
				head = node.down;//移除顶端,下面的上来
				head.up = null;
			}else if (node == tail) {
				tail = node.up;
				tail.down = null;
			}else {
				node.up.down = node.down;
				node.down.up = node.up;
			}
		}
		//移除该节点
		node.up = null;
		node.down = null;
	}
	
}

// removeNodeList刚刚减少一个节点的桶
// 判断这个桶是不是已经空了
// 如果已经空了,再根据removeNodeList他是不是最左边的桶
// 如果是让removeNodeList他下一个桶做头桶,如果不是删掉后保证左右相连
// 函数返回值表示这个桶减少一个节点后是不是空桶
public boolean modifyHeadList(NodeList removeNodeList) {
	if (removeNodeList.isEmpty()) {
		if (headList == removeNodeList) {
			headList = removeNodeList.next;
			if (headList != null) {
				headList.last = null;// 新头桶左边指向空
			}
		} else {
			removeNodeList.last.next = removeNodeList.next;
			if (removeNodeList.next != null) {
				removeNodeList.next.last = removeNodeList.last;
			}
		}
		return true;
	}
	return false;
}

// 放入一个节点,
// 首先查看是否一个桶都没有,先创建
// 找桶放值,找不到创建
// 创建保证左右桶相连
// 找到桶,值满要删tail,遵循FIFO原则
public void put(int key, int value) {
	if (capacity == 0) {
		return;
	}
	if (records.containsKey(key)) {// 修改
		Node node = records.get(key);
		node.value = value;
		node.times++;// 修改词频
		NodeList curNodeList = heads.get(node);
		move(node, curNodeList);
	} else {
		if (size == capacity) {// 桶满的情况
			Node node = headList.tail;
			// 删除节点并对桶判空
			headList.deleteNode(node);
			modifyHeadList(headList);
			// 总map要删,heads标记桶的map也要删
			records.remove(node.key);
			heads.remove(node);
			size--;
		}
		// 放节点
		Node node = new Node(key, value, 1);
		if (headList == null) {// 没有桶,新建一个放节点就行
			headList = new NodeList(node);
		} else {// 寻找旧桶
			if (headList.head.times.equals(node.times)) {
				headList.addNodeFromHead(node);// 替换旧头部
			} else {// 找不到旧桶新建一个,左右连接一下,比如词频1的是空的,原来的都到别的桶上去了
				NodeList newList = new NodeList(node);
				newList.next = headList;
				headList.last = newList;
				headList = newList;// 新建并作为头桶
			}
		}
		// 总map和桶标记map都放入节点,容量++
		records.put(key, node);
		heads.put(node, headList);
		size++;
	}
}

public int get(int key) {
	if (!records.containsKey(key)) {
		return -1;
	}
	Node node = records.get(key);
	node.times++;
	NodeList curNodeList = heads.get(node);
	move(node, curNodeList);
	return node.value;
}

// 词频变化,换桶
// 把这个节点从原来的同移除
// 过程要保证任然是双向链表
private void move(Node node, NodeList oldNodeList) {
	oldNodeList.deleteNode(node);
	// oldNodeList原来的可能拿走这个节点后是个空桶
	// oldNodeList可能是中间的桶,所以还要保证空桶后要连接前后连个同
	// preList表示+1的桶的前一个桶是谁
	// 没有节点就用前一个桶
	NodeList preList = modifyHeadList(oldNodeList) ? oldNodeList.last : oldNodeList;
	// nextList表示次数+1的桶的后一个桶是谁
	NodeList nextList = oldNodeList.next;
	if (nextList == null) {// 后面没有桶
		NodeList newList = new NodeList(node);
		if (preList != null) {
			preList.next = newList;
		}
		newList.last = preList;
		if (headList == null) {
			headList = newList;
		}
		heads.put(node, newList);
	} else { // +1后有桶
		if (nextList.head.times.equals(node.times)) {// 找到同词频
			nextList.addNodeFromHead(node);
			heads.put(node, nextList);
		} else {// 找不到同词频,就新建
			NodeList newList = new NodeList(node);
			if (preList != null) {
				preList.next = newList;
			}
			newList.last = preList;
			newList.next = nextList;
			nextList.last = newList;
			if (headList == nextList) {
				headList = newList;
			}
			heads.put(node, newList);//重新标记桶
		}
	}
}

}

标签:node,map,headList,NodeList,LFU,链表,节点,public,Node
From: https://www.cnblogs.com/15078480385zyc/p/17686269.html

相关文章

  • 剑指 Offer 22. 链表中倒数第k个节点
    输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。示例:给定一个链表:1->2->3->4->5,和k=......
  • 实现Map批量赋值,我只需24秒搞定!
    函数的功能是将一组键值对批量赋值给Map中的键。在Java中,通常使用Map的put方法逐个将键值对赋值给Map,但在某些场景下,可能需要一次性将多个键值对赋值给Map。函数功能:Map批量赋值参数1:参数名称:target;参数类型:Map;参数描述:Map对象参数2:参数名称:keyAndValue;参数类型:Object;参数描述:k......
  • LRUCache算法缓存策略(map+doubleLinkedList)
    packagearithmetic;importjava.util.HashMap;publicclassFaceTest81{//LRUcache缓存策略map+双向链表//get、update、put需要时间复杂度达到O1//map+双向链表结构publicFaceTest81(intcapacity){ cache=newMyCache(capacity);}privateMyCache<Integer,Intege......
  • @supermap/iclient-leaflet使用tiledMapLayer报错
    使用leaflet加载超图的时候有时候超图无法加载有时候报如下错误因为手上有好几个项目都在使用leaflet但是同样都使用@supermap/iclient-leaflet(版本11.1.0-a)加载超图,有的项目可行,有的不可行最后打开项目根目录下node_modules里查看@supermap文件夹里的iclient-leafl......
  • Redis复习:(1)RedisTempalte之BitMap操作
    packagecn.edu.tju.service.impl;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.dao.DataAccessException;importorg.springframework.data.redis.connection.RedisConnection;importorg.springframework.data.redis.co......
  • map、sync.map、concurrent-map适用场景与源码解析
    最近一直加班,无论工作日还是周末,虽然每天很忙但总感觉空空的,很少有时间停下来思考与总结。项目中各种甩锅,最后最苦逼的还是落到了研发的头上,文档编写、环境部署、问题排查虐得一遍又一遍。事情杂乱,研发效率超级低,不知道何是是个头呀背景在go中,map是最常用的集合之一。其底层key存......
  • hibernate怎么实现一个类对象map多个表名
    1)映射文件在一个映射文件中定义class和table的对应关系,用entity-name来区分不同的映射:<class=”MyClass”entity-name=”testA”table=”mytable_A”><propertyname=”name”column=”st_name”/>……</class><class=”MyClass”entity-name=”testB”table=”mytable......
  • Hadoop Map/Reduce教程
    【目的】       这篇教程从用户的角度出发,全面地介绍了HadoopMap/Reduce框架的各个方面。【先决条件】       请先确认Hadoop被正确安装、配置和正常运行中。更多信息见:       Hadoop快速入门对初次使用者。       Hadoop集群搭建对大规模分布式......
  • HashMap、LinkedHashMap和TreeMap:你真的了解它们吗?
    亲爱的小伙伴们,大家好呀!我是小米,一个热衷于技术分享的90后程序员。今天我要和大家聊聊一个在面试中经常会被问到的话题:HashMap、LinkedHashMap、TreeMap的区别。这可是一个非常重要的知识点,不仅在面试中会被频繁提及,而且在实际开发中也经常用到。让我们一起深入了解这三者的异同吧!H......
  • ubuntu20安装colmap
     教程https://colmap.github.io/install.html 前提r900k 3070显卡cuda11.5opencv3.4.9如果有acoda先从环境变量去掉,以免导致多重库问题 起作用source~/.bashrcgcc11 g++ 11 安装sudoapt-getinstall\git\cmake\ninja-bui......