天天看点

用双向链表实现内存置换算法(四)

用双向链表实现内存置换算法(四)

这一篇是使用双向链表实现第三个内存置换算法----LFU

LFU算法分析:

最不经常使用算法,在缓存满了的时候,先淘汰使用频率最少的,这里我们就需要用一个变量来记录数据的使用频率。但是会有一个情况,就是使用频率相同的时候如何去处理,这里我是采用map来记录,key为使用的频率,value是双向链表,当缓存满了的时候,找到最低频率的value,根据FIFO的规则去淘汰。

  • 第一步,我们先给每一个节点增加一个记录频率的属性
class LFUNode(Node):
    def __init__(self, key, value):
        self.freq = 0
        super(LFUNode, self).__init__(key, value)
           
  • 第二步,实现LFU算法
    • 这里需要增加一个update()方法来更新节点的频率

这里直接附上代码,在结尾会对一些细节进行解释:

class LFUCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.map = {}
        # key: 频率, value: 频率对应的双向链表
        self.freq_map = {}
        self.size = 0

    # 更新节点频率的操作
    def __update_freq(self, node):
        freq = node.freq

        # 删除
        node = self.freq_map[freq].remove(node)
        if self.freq_map[freq].size == 0:
            del self.freq_map[freq]

        # 更新
        freq += 1
        node.freq = freq
        if freq not in self.freq_map:
            self.freq_map[freq] = DoubleLinkedList()
        self.freq_map[freq].append(node)

    def get(self, key):
        if key not in self.map:
            return -1
        node = self.map.get(key)
        self.__update_freq(node)
        return node.value

    def put(self, key, value):
        if self.capacity == 0:
            return

        # 缓存命中
        if key in self.map:
            node = self.map.get(key)
            node.value = value
            self.__update_freq(node)
        # 缓存未命中
        else:
            if self.capacity == self.size:
                # 最低频率
                min_freq = min(self.freq_map)
                node = self.freq_map[min_freq].pop()
                del self.map[node.key]
                self.size -= 1
            node = LFUNode(key, value)
            node.freq = 1
            self.map[key] = node
            if node.freq not in self.freq_map:
                self.freq_map[node.freq] = DoubleLinkedList()
            self.freq_map[node.freq].append(node)
            self.size += 1
           
  • init: 在初始化中增加了一个记录频率的map
  • get: get方法和之前一样,没有key直接返回-1,有就返回数据
  • update: 在进行put的时候,node存在,则先将原来的node删除,将这个频率+1,再放入相应的链表中。
  • put: 缓存命中时,更新数据使用频率,缓存未命中,如果缓存未满,就放在频率为1的链表中,如果缓存满了,则把使用频率最低的链表根据FIFO原则替换。