赞
踩
这道题可以用两个哈希表来实现,一个hash表(kvMap)用来存储节点,另一个hash表(freqMap)用来存储双向链表,链表的头节点代表最近使用的元素,离头节点越远的节点代表最近最少使用的节点。
注意:freqMap 的 key 为节点的使用频次。
下图是freqMap的结构:
这是kvMap: 它的key没有什么特殊含义,value是储存的节点
题目中调用get方法会增加元素的使用次数(freq),这时在freqMap中需要将该节点转移到对应的位置上(比如该节点使用了两次,那就把该节点转移到key为2的位置上去)
put方法分为两种情况:如果要添加的key不存在,说明是新元素,放到key为1的位置上,如果key存在,此时是修改操作,改变元素的value值,对应的频次+1,同样放到对应的位置上。
淘汰策略:要保证两个hash表中的节点数一致。
- package leetcode;
-
- import java.util.HashMap;
-
- //设计LFU缓存
- public class LFUCacheLeetcode460 {
-
- static class LFUCache {
-
- static class Node{
- Node prev;
- Node next;
- int key;
- int value;
- int freq = 1;
- public Node(){
-
- }
- public Node(int key, int value) {
- this.key = key;
- this.value = value;
- }
- }
-
- static class DoublyLinkedList{
- Node head;
- Node tail;
- int size;
-
- public DoublyLinkedList(){
- head = tail = new Node();
- head.next = tail;
- tail.prev = head;
- }
-
- //头部添加 head<->1<->2<->tail 添加3
- public void addFirst(Node node){
- Node oldFirst = head.next;
- oldFirst.prev = node;
- head.next = node;
- node.prev = head;
- node.next = oldFirst;
- size++;
- }
-
- //指定节点删除 head<->1<->2<->3<->tail 删除2
- public void remove(Node node){
- Node prev = node.prev;
- Node next = node.next;
- prev.next = next;
- next.prev = prev;
- size--;
- }
-
- //删除最后一个节点
- public Node removeLast(){
- Node last = tail.prev;
- remove(last);
- return last;
- }
-
- //判断双向链表是否为空
- public boolean isEmpty(){
- return size == 0;
- }
- }
-
- private HashMap<Integer,Node> kvMap = new HashMap<>();
- private HashMap<Integer,DoublyLinkedList> freqMap = new HashMap<>();
- private int capacity;
- private int minFreq = 1;
- public LFUCache(int capacity) {
- this.capacity = capacity;
- }
-
- /*
- * 获取节点 不存在:返回 -1
- * 存在: 增加节点的使用频次,将其转移到频次+1的链表中
- * 判断旧节点所在的链表是否为空,更新最小频次minFreq
- * */
- public int get(int key) {
- if(!kvMap.containsKey(key)){
- return -1;
- }
- Node node = kvMap.get(key);
- //先删除旧节点
- DoublyLinkedList list = freqMap.get(node.freq);
- list.remove(node);
- //判断当前链表是否为空,为空可能需要更新最小频次
- if(list.isEmpty() && node.freq == minFreq){
- minFreq++;
- }
- node.freq++;
- //将新节点放入新位置,可能该频次对应的链表不存在,不存在需要创建
- freqMap.computeIfAbsent(node.freq, k -> new DoublyLinkedList())
- .addFirst(node);
- return node.value;
- }
-
- /*
- * 更新
- * 将节点的value更新,增加节点的使用频次,将其转移到频次+1的链表中
- * 新增
- * 检查是否超过容量,若超过,淘汰minFreq链表的最后节点
- * 创建新节点,放入kvMap,并加入频次为 1 的双向链表
- * */
- public void put(int key, int value) {
- if(kvMap.containsKey(key)){ //更新
- Node node = kvMap.get(key);
- DoublyLinkedList list = freqMap.get(node.freq);
- list.remove(node);
- if(list.isEmpty() && node.freq == minFreq){
- minFreq++;
- }
- node.freq++;
- freqMap.computeIfAbsent(node.freq, k -> new DoublyLinkedList())
- .addFirst(node);
- node.value = value;
- }else{ //新增
- //先判断容量是否已满
- if(kvMap.size() == capacity){
- //执行淘汰策略
- Node node = freqMap.get(minFreq).removeLast();
- kvMap.remove(node.key);
- }
- Node node = new Node(key, value);
- kvMap.put(key, node);
- //加入频次为1的双向链表
- freqMap.computeIfAbsent(1, k -> new DoublyLinkedList())
- .addFirst(node);
- minFreq = 1;
- }
- }
- }
-
- public static void main(String[] args) {
- LFUCache cache = new LFUCache(2);
- cache.put(1, 1);
- cache.put(2, 2);
- System.out.println(cache.get(1)); // 1 freq=2
- cache.put(3, 3);
- System.out.println(cache.get(2)); // -1
- System.out.println(cache.get(3)); // 3 freq=2
- cache.put(4, 4);
- System.out.println(cache.get(1)); // -1
- System.out.println(cache.get(3)); // 3 freq=3
- System.out.println(cache.get(4)); // 4 freq=2
-
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。