内存淘汰机制——LRU与LFU

内存淘汰机制之LRU与LFU

  • LRU(Least Recently Used):淘汰 近期最不会访问的数据
  • LFU(Least Frequently Used):淘汰 最不经常使用(访问次数少)

所谓淘汰就是将内存中指定部分的数据移除,释放空间提供给新来的数据。

LRU

LeetCode入口👉👉👉No.146

image-20200505211459196

  • 存数据,将数据插入链表头部;如果内存满了,需要先将链表尾部数据删除,再插入

  • 取数据,每次将取到的数据重新放到链表头部

LRU一般使用哈希链表(哈希表+双向链表)实现,可以在 \(O(1)\) 复杂度内实现插入、删除。

OrderedDict 的 popitem 方法默认删除并返回的是字典里的最后一个元素;popitem(last=False) 删除并返回第一个被添加进去的元素

coding:

#--python
#使用python自带哈希链表(有序字典OrderedDict)实现
from collections import OrderedDict
class LRUCache(OrderedDict):

    def __init__(self, capacity: int):
        self.capacity = capacity

    def get(self, key: int) -> int:
        if key not in self:
            return -1
        self.move_to_end(key)
        return self[key]

    def put(self, key: int, value: int) -> None:
        if key in self:
            self.move_to_end(key)
        self[key] = value	#更新value
        if len(self) > self.capacity:
            self.popitem(last=False)

自己实现哈希链表

#--python
#定义双向链表节点
class DLinkNode():
    def __init__(self,key=0,value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache():
    def __init__(self,capacity):
        self.cache = {}
        self.size = 0
        self.capacity = capacity
        #定义伪头部 伪尾部节点
        self.head,self.tail = DLinkNode(),DLinkNode()
        self.head.next = self.tail
        self.tail.prev = self.head
    
    #模拟OrderedDict 定义 添加 删除 移动头部方法
    def _add_node(self,node):
        node.prev = self.head
        node.next = self.head.next

        self.head.next.prev = node
        self.head.next = node
    
    def _remove_node(self,node):
        node.next.prev = node.prev
        node.prev.next = node.next
    
    def _move_to_head(self,node):
        self._remove_node(node)
        self._add_node(node)

    def _pop_tail(self):
        node = self.tail.prev
        self._remove_node(node)
        return node

    def get(self,key):
        if key not in self.cache:
            return -1
        node = self.cache[key]
        self._move_to_head(node)
        return node.value
    
    def put(self,key,value):
        #如果key不存在,创建node 添加
        if key not in self.cache:
            node = DLinkNode(key,value)
            self.cache[key] = node
            self._add_node(node)
            self.size += 1
            #如果满了,删除双向链表节点 和 字典对应键
            if self.size > self.capacity:
                node = self._pop_tail()
                self.cache.pop(node.key)
                self.size -= 1
        else:
            node = self.cache[key]
            node.value = value
            self._move_to_head(node)

LFU

LeetCode入口👉👉👉No.460

image-20200505211548831

  • 维护一个访问频次的数据结构,取数据,访问频次加一,根据访问次数排序
  • 存数据,当缓存满时,淘汰点访问次数最小的

使用双哈希表 keyMap 和 freqMap

coding:

#--python
#双哈希表 
class Node:
    def __init__(self, key, val, pre=None, nex=None, freq=0):
        self.pre = pre
        self.nex = nex
        self.freq = freq
        self.val = val
        self.key = key
        
    def insert(self, nex):
        nex.pre = self
        nex.nex = self.nex
        self.nex.pre = nex
        self.nex = nex
    
def create_linked_list():
    head = Node(0, 0)
    tail = Node(0, 0)
    head.nex = tail
    tail.pre = head
    return (head, tail)

class LFUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.size = 0
        self.minFreq = 0
        self.freqMap = collections.defaultdict(create_linked_list)
        self.keyMap = {}

    def delete(self, node):
        if node.pre:
            node.pre.nex = node.nex
            node.nex.pre = node.pre
            if node.pre is self.freqMap[node.freq][0] and node.nex is self.freqMap[node.freq][-1]:
                self.freqMap.pop(node.freq)
        return node.key
        
    def increase(self, node):
        node.freq += 1
        self.delete(node)
        self.freqMap[node.freq][-1].pre.insert(node)
        if node.freq == 1:
            self.minFreq = 1
        elif self.minFreq == node.freq - 1:
            head, tail = self.freqMap[node.freq - 1]
            if head.nex is tail:
                self.minFreq = node.freq

    def get(self, key: int) -> int:
        if key in self.keyMap:
            self.increase(self.keyMap[key])
            return self.keyMap[key].val
        return -1

    def put(self, key: int, value: int) -> None:
        if self.capacity != 0:
            if key in self.keyMap:
                node = self.keyMap[key]
                node.val = value
            else:
                node = Node(key, value)
                self.keyMap[key] = node
                self.size += 1
            if self.size > self.capacity:
                self.size -= 1
                deleted = self.delete(self.freqMap[self.minFreq][0].nex)
                self.keyMap.pop(deleted)
            self.increase(node)
posted @ 2020-05-25 16:22  鱼与鱼  阅读(1748)  评论(0编辑  收藏  举报