LFU两种解法

设计并实现最不经常使用(LFU)缓存的数据结构。它应该支持以下操作:get 和 put。

get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1。
put(key, value) - 如果键不存在,请设置或插入值。当缓存达到其容量时,它应该在插入新项目之前,使最不经常使用的项目无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,最近最少使用的键将被去除。

一个项目的使用次数就是该项目被插入后对其调用 get 和 put 函数的次数之和。使用次数会在对应项目被移除后置为 0。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lfu-cache

  • 解法1:通过优先队列,将node节点按照出现次数从小到大排序。在用一个map存储node节点。

    put时,1.若map中存在该key,则更新key值,在优先队列中找到key对应的node,先删除在增加,达到重新排序的效果。

       2.若map中不存在key,则在map中新建并存储node。在将node存入优先队列之前要先判断优先队列的大小。若大小等于capacity,则限制性queue.poll()。

       注意node节点重写compareTo方法时,在次数相同的情况下,要比较存入的先后顺序。所以引入idx变量。

代码如下。时间复杂度为O(logN)

 1 package Leetcode;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 import java.util.PriorityQueue;
 6 import java.util.Queue;
 7 
 8 class LFUCache1 {
 9 
10     class Node implements Comparable<Node> {
11         int key;
12         int value;
13         int freq;
14         int idx;
15 
16         public Node() {}
17 
18         public Node(int key, int value, int idx) {
19             this.key = key;
20             this.value = value;
21             freq = 1;
22             this.idx = idx;
23         }
24 
25         @Override
26         public int compareTo(Node node) {
27             int diff = freq - node.freq;
28             return diff != 0? diff: idx - node.idx;
29         }
30     }
31 
32     Map<Integer, Node> cache;
33     Queue<Node> queue;
34     int capacity;
35     int size;
36     int idx = 0;
37 
38     public LFUCache1(int capacity) {
39         cache = new HashMap<>(capacity);
40         if (capacity > 0) {
41             queue = new PriorityQueue<>(capacity);
42         }
43         this.capacity = capacity;
44     }
45 
46     public int get(int key) {
47         Node node = cache.get(key);
48         if (node == null) {
49             return -1;
50         }
51         node.freq++;
52         node.idx = idx++;
53         queue.remove(node);
54         queue.offer(node);
55         return node.value;
56 
57     }
58 
59     public void put(int key, int value) {
60         if (capacity == 0) {
61             return;
62         }
63         Node node = cache.get(key);
64         if (node != null) {
65             node.value = value;
66             node.freq++;
67             node.idx = idx++;
68             queue.remove(node);
69             queue.offer(node);
70         } else {
71             if (size == capacity) {
72                 cache.remove(queue.peek().key);
73                 queue.poll();
74                 size--;
75             }
76             Node newNode = new Node(key, value, idx++);
77             cache.put(key, newNode);
78             queue.offer(newNode);
79             size++;
80         }
81     }
82 
83     public static void main(String[] args) {
84         LFUCache1 lfu = new LFUCache1(3);
85         lfu.put(1,1);
86         lfu.put(2,2);
87         lfu.put(3,3);
88         lfu.put(4,4);
89     }
90 }
  • 解法2利用Map<Integer, LinkedHashSet<Node>>,其中key含义为出现的次数,value为出现主键次数的key的集合。这里一开始想用Queue,但是从中删除非第一位数时的时间复杂度为O(n)。这个方法中,需要变量min取得当前最小出现次数。

代码中91到94行含义为方法一种的queue.poll,思路类似,时间复杂度为O(1)

代码如下

 1 package Leetcode;
 2 
 3 import java.util.HashMap;
 4 import java.util.LinkedHashSet;
 5 import java.util.Map;
 6 import java.util.Queue;
 7 
 8 class LFUCache {
 9     class Node {
10         int key;
11         int value;
12         int freq = 1;
13 
14         public Node() {}
15 
16         public Node(int key, int value) {
17             this.key = key;
18             this.value = value;
19         }
20     }
21     Map<Integer, Node> cache;  // 存储缓存的内容
22     Map<Integer, LinkedHashSet<Node>> freqMap; // 存储每个频次对应的双向链表
23     int size;
24     int capacity;
25     int min; // 存储当前最小频次
26 
27     public LFUCache(int capacity) {
28         cache = new HashMap<> (capacity);
29         freqMap = new HashMap<>();
30         this.capacity = capacity;
31     }
32 
33     public int get(int key) {
34         Node node = cache.get(key);
35         if (node == null) {
36             return -1;
37         }
38         freqInc(node);
39         return node.value;
40     }
41 
42     public void put(int key, int value) {
43         if (capacity == 0) {
44             return;
45         }
46         Node node = cache.get(key);
47         if (node != null) {
48             node.value = value;
49             freqInc(node);
50         } else {
51             if (size == capacity) {
52                 Node deadNode = removeNode();
53                 cache.remove(deadNode.key);
54                 size--;
55             }
56             Node newNode = new Node(key, value);
57             cache.put(key, newNode);
58             addNode(newNode);
59             size++;
60         }
61     }
62 
63     void freqInc(Node node) {
64         // 从原freq对应的链表里移除, 并更新min
65         int freq = node.freq;
66         LinkedHashSet<Node> set = freqMap.get(freq);
67         set.remove(node);
68         if (freq == min && set.size() == 0) {
69             min = freq + 1;
70         }
71         // 加入新freq对应的链表
72         node.freq++;
73         LinkedHashSet<Node> newSet = freqMap.get(freq + 1);
74         if (newSet == null) {
75             newSet = new LinkedHashSet<>();
76             freqMap.put(freq + 1, newSet);
77         }
78         newSet.add(node);
79     }
80 
81     void addNode(Node node) {
82         LinkedHashSet<Node> set = freqMap.get(1);
83         if (set == null) {
84             set = new LinkedHashSet<>();
85             freqMap.put(1, set);
86         }
87         set.add(node);
88         min = 1;
89     }
90 
91     Node removeNode() {
92         LinkedHashSet<Node> set = freqMap.get(min);
93         Node deadNode = set.iterator().next();
94         set.remove(deadNode);
95         return deadNode;
96     }
97 }

猜你喜欢

转载自www.cnblogs.com/zhangbochao/p/12639022.html
今日推荐