牛客题霸每日一题LRU缓存结构设计,关联LFU缓存结构设计
LRU: Map + 自定义双向链表
import java.util.*; public class Solution { /** * lru design * @param operators int整型二维数组 the ops * @param k int整型 the k * @return int整型一维数组 */ public int[] LRU (int[][] operators, int k) { // write code here LRU lru = new LRU(k); List<Integer> res = new ArrayList<>(operators.length); for(int[] oper: operators){ int op = oper[0]; if(oper[0] == 2){ res.add(lru.get(oper[1])); } else{ lru.set(oper[1], oper[2]); } } int[] result = new int[res.size()]; for(int i = 0; i < res.size(); ++i){ result[i] = res.get(i); } return result; } private class LRU{ private class Node{ int key, val; Node prev, next; public Node(int k, int v, Node p, Node n){ key = k; val = v; prev = p; next = n; } } private int MAXSIZE; private Node head; private Node tail; private Map<Integer, Node> map; public LRU(int k){ MAXSIZE = k; head = new Node(0, 0, null, null); tail = new Node(0, 0, head, null); head.next = tail; map = new HashMap<>(); } public int get(Integer key){ Node curr = map.getOrDefault(key, null); if(curr == null){return -1;} remove(curr); insert(curr); return curr.val; } public void set(Integer key, Integer val){ Node curr = map.getOrDefault(key, null); Node newNode = new Node(key, val, null, null); map.put(key, newNode); insert(newNode); if(curr != null){ remove(curr); }else{ if(map.size() > MAXSIZE){ Node oldest = tail.prev; map.remove(oldest.key); remove(oldest); } } } private void remove(Node curr){ curr.prev.next = curr.next; curr.next.prev = curr.prev; } private void insert(Node curr){ curr.next = head.next; curr.next.prev = curr; curr.prev = head; head.next = curr; } } }
LFU第一种实现: Map+单个双向链表
import java.util.*; public class Solution { /** * lfu design * @param operators int整型二维数组 ops * @param k int整型 the k * @return int整型一维数组 */ public int[] LFU (int[][] operators, int k) { // write code here if(operators == null || operators.length == 0){return new int[0];} List<Integer> res = new ArrayList<>(10); MyLFU lfu = new MyLFU(k); for(int[] oper : operators){ if(oper.length == 3){ lfu.set(oper[1], oper[2]); }else{ res.add(lfu.get(oper[1])); } } int len = res.size(); int[] arr = new int[len]; for(int i = 0; i < len; ++i){ arr[i] = res.get(i); } return arr; } private class MyLFU{ private class Node{ public int key; public int val; public int count; public Node next; public Node prev; public Node(int k, int v){ this.key = k; this.val = v; this.count = 1; } } private Node head = new Node(0, 0); // next指向删除概率最小的元素 private Node tail = new Node(0, 0); // prev指向删除该旅最大的元素 private int size = 0; private int capacity; private Map<Integer, Node> nodes = new HashMap<>(16); public MyLFU(int k){ this.head.next = tail; this.tail.prev = head; this.capacity = k; } public int get(int key){ if(this.nodes.containsKey(key)){ Node node = this.nodes.get(key); ++node.count; this.adjustToHead(node); return node.val; } return -1; } public void set(int key, int val){ if(this.nodes.containsKey(key)){ Node node = this.nodes.get(key); node.val = val; ++node.count; this.adjustToHead(node); }else{ Node node = new Node(key, val); Node curr = this.head; while(curr.next != tail && curr.next.count > 1){ curr = curr.next; } node.next = curr.next; curr.next.prev = node; curr.next = node; node.prev = curr; this.nodes.put(key, node); if(++this.size > this.capacity){ this.remove(); } } } private void adjustToHead(Node node){ Node prev = node.prev; while(prev != head && node.count >= prev.count){ prev.next = node.next; node.next.prev = prev; node.next = prev; node.prev = prev.prev; prev.prev.next = node; prev.prev = node; prev = node.prev; } } private void remove(){ this.nodes.remove(this.tail.prev.key); this.tail.prev.prev.next = this.tail; this.tail.prev = this.tail.prev.prev;; } } }
LFU第二种实现:Map1存key->Node,Map2存频数->双向链表,同一双向链表里的节点的频数相同。该方法时间复杂度略优于第一种实现。
import java.util.*; public class Solution { /** * lfu design * @param operators int整型二维数组 ops * @param k int整型 the k * @return int整型一维数组 */ public int[] LFU (int[][] operators, int k) { // write code here if(operators == null || operators.length == 0){return new int[0];} List<Integer> res = new ArrayList<>(10); MyLFU lfu = new MyLFU(k); for(int[] oper : operators){ if(oper.length == 3){ lfu.set(oper[1], oper[2]); }else{ res.add(lfu.get(oper[1])); } } int len = res.size(); int[] arr = new int[len]; for(int i = 0; i < len; ++i){ arr[i] = res.get(i); } return arr; } private class MyLFU{ private class Node{ public int key; public int val; public int count; public Node next; public Node prev; public Node(int k, int v){ this.key = k; this.val = v; this.count = 0; } } private class MyLinkedList{ private Map<Integer, Node> map = new HashMap<>(16); private Node head = new Node(0, 0); // head.next为最后删除的元素 private Node tail = new Node(0, 0); // tail.prev为优先删除的元素 private int size = 0; public MyLinkedList(){ this.head.next = this.tail; this.tail.prev = this.head; } public int size(){return this.size;} public Node pop(){ Node node = this.tail.prev; if(node == this.head){return null;} node.prev.next = this.tail; this.tail.prev = node.prev; map.remove(node.key); --size; return node; } public void add(Node node){ node.next = this.head.next; node.next.prev = node; this.head.next = node; node.prev = this.head; map.put(node.key, node); ++size; } public void removByKey(int key){ if(!this.map.containsKey(key)){return;} Node node = map.get(key); node.prev.next = node.next; node.next.prev = node.prev; map.remove(key); --size; } } private int size = 0; private int minCount = 1; private int capacity; private Map<Integer, Node> nodes = new HashMap<>(16); // key->Node private Map<Integer, MyLinkedList> freqs = new HashMap<>(16); // count->MyLinkedList public MyLFU(int k){ this.capacity = k; } public int get(int key){ if(this.nodes.containsKey(key)){ Node node = this.nodes.get(key); this.move(node); return node.val; } return -1; } public void set(int key, int val){ Node node; if(!this.nodes.containsKey(key)){ node = new Node(key, val); this.nodes.put(key, node); ++this.size; }else{ node = this.nodes.get(key); node.val = val; } this.move(node); if(this.size > this.capacity){ MyLinkedList list = this.freqs.get(this.minCount); Node poped = list.pop(); this.nodes.remove(poped.key); if(list != null || list.size() == 0){ this.minCount = 0; do{ list = this.freqs.getOrDefault(++this.minCount, null); }while(list == null || list.size() == 0); } --this.size; } } private void move(Node node){ MyLinkedList list = this.freqs.getOrDefault(node.count, null); if(list != null){ list.removByKey(node.key); } int newCount = ++node.count; if(!this.freqs.containsKey(newCount)){ list = new MyLinkedList(); this.freqs.put(newCount, list); }else{ list = this.freqs.get(newCount); } list.add(node); } } }