# 460. LFU Cache

{% embed url="<https://leetcode.com/problems/lfu-cache/>" %}

跟LRU那题相比，排序的 primary key 变成了使用次数 frequency，有并列再去比较 recent use，本来想在LRU解法的基础上增加一个`freq` map，但是这样的话，如果拿 frequency 做 key 则`get`函数做不到O(1)，拿 node 做 key 则`put`函数做不到O(1)。

{% content-ref url="146.-lru-cache" %}
[146.-lru-cache](https://xuqiangwen1994.gitbook.io/practice/146.-lru-cache)
{% endcontent-ref %}

然后就看到了这个大神讲解：[Python concise solution **detailed** explanation: Two dict + Doubly linked list](https://leetcode.com/problems/lfu-cache/discuss/207673/Python-concise-solution-**detailed**-explanation%3A-Two-dict-%2B-Doubly-linked-list)

基本思路是，`freq` map 存储一个双指针链表（python可以用`OrderedDict`代替），这个链表内部来完成LRU的功能，这样在`put`的时候就可以先找LFU，里面有多个元素的话可以直接根据顺序找到LRU（其实就是嵌套一个LRU的解）。

{% tabs %}
{% tab title="OrderedDict" %}

```python
class ListNode:
    def __init__(self, key, value):
        self.key = key
        self.val = value
        self.freq = 1
        
class LFUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = dict() 
        ## usage[f][k]=v : frequency = f, key = k, v = value 
        self.usage = collections.defaultdict(collections.OrderedDict)
        ## current least frequenct usage
        self.LF = 0

    def get(self, key: int) -> int:
        if key not in self.cache: return -1
        node = self.cache[key]
        ## update the frequency
        self.update(node, node.val)
        return node.val
        
    def put(self, key: int, value: int) -> None:
        if self.capacity == 0: return
        if key not in self.cache: 
            if len(self.cache) >= self.capacity:
                ## pop the node with current least freuenct usage (FIFO)
                k, v = self.usage[self.LF].popitem(last=False)
                self.cache.pop(k)
            node = ListNode(key, value)
            ## save the new node into cache and usage map
            self.cache[key] = node
            self.usage[1][key] = value
            ## reset current LF to 1
            self.LF = 1
        else: 
            ## update the vaLue of existing key 
            node = self.cache[key]
            node.val = value
            ## update the frequency
            self.update(node, value)
            
            
    def update(self, node, newVal):
        k, f = node.key, node.freq
        ## delete from the former frequency (f)
        self.usage[f].pop(k)
        ## if the former frequency is the LFU and it become empty
        ## the new frequency (f+1) become new LFU
        if not self.usage[f] and self.LF == f:
            self.LF += 1
        ## push to the new frequency (f+1)
        self.usage[f+1][k] = newVal
        node.freq += 1
        
```

{% endtab %}

{% tab title="double linked node" %}

```
```

{% endtab %}
{% endtabs %}
