第一题
上一题实现了LRU缓存算法,LFU也是一个著名的缓存算法
自行了解之后实现LFU中的set 和 get
要求:两个方法的时间复杂度都为O(1)
LFU根据get、set操作次数决定的优先级。
同样次数,最不经常访问的先出去。
实现思路:建立一个次数链,每个次数再链接上一个双向链。(两个双链表)
Put和Get的时候,先检查是否存在
如果没有,put就存在1的链表下,get就返回null。
如果有,找到属于哪个头,然后分离出来,查看头部的下一个是否次数+1的关系,有就插入,没有就建出来。每次挂都是挂在小链表的头部。
size满了,就删最左边底下的head。
看代码...(代码是把最近操作的放在头部)
public class Code_03_LFU { //小链表的节点 public static class Node { public Integer key; public Integer value; public Integer times; public Node up; public Node down; public Node(int key, int value, int times) { this.key = key; this.value = value; this.times = times; } } public static class LFUCache { //把次数相同的节点连在一起的链表 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; } //其中的任何节点都可能删,因为次数增加也要调整节点位置 //把节点从本环境中分离 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; } } private int capacity;//容量 private int size;//当前大小 //通过key找Node private HashMap<Integer, Node> records; //找到Node的当前所在链表 private HashMap<Node, NodeList> heads; private NodeList headList; public LFUCache(int capacity) { this.capacity = capacity; this.size = 0; this.records = new HashMap<>(); this.heads = new HashMap<>(); headList = null; } public void set(int key, int value) { if (records.containsKey(key)) {//存在 Node node = records.get(key); node.value = value; node.times++; NodeList curNodeList = heads.get(node); move(node, curNodeList);//帮node找新家 } else { if (size == capacity) {//腾出空间 Node node = headList.tail; headList.deleteNode(node); modifyHeadList(headList);//检查是否要调整(有可能删光了) 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 {//没有就建 NodeList newList = new NodeList(node); newList.next = headList; headList.last = newList; headList = newList; } } //记录信息 records.put(key, node); heads.put(node, headList); size++; } } private void move(Node node, NodeList oldNodeList) { oldNodeList.deleteNode(node);//先从老家搬出