python实现LRU置换算法、LFU置换算法(补充self.map的使用和字典的方法) LRU(最近最少使用)算法: LFU(最不经常使用)算法:

关于本地存放的映射关系map:

本质上是一个空字典:self.map={}

每次新增一个节点node,都会把node的key, value放入map中: self.map[key] = node ==> self.map的样子应该是{key1:node1, key2:node2}  (注意:node是一个自定义的新的类,详见:https://www.cnblogs.com/marvintang1001/p/11125619.html )

取出字典一个键值对(返回的是值): node = self.map[key]  这是非安全方法,如果没有这个key会报错。安全方法:value = my_dict.get("key")  如果没有这个键就返回None。) 

删除字典一对键值的方法: self.map.pop(key)  或者是  del self.map[key]

for k, v in self.freq_map.items():  字典的items方法:返回可遍历的(键, 值) 元组数组

*关于字典的应用:https://www.cnblogs.com/lowmanisbusy/p/9142199.html


每次使用,把使用的节点放到链表最前面

淘汰缓存时,把链表尾部的节点淘汰

#! -*- encoding=utf-8 -*-

from computer_principle.doublelinkedlist import DoubleLinkedList, Node


class LRUCache(object):

    def __init__(self, capacity):
        self.capacity = capacity
        self.map = {}  # 保存key和value的映射关系
        self.list = DoubleLinkedList(self.capacity)

    # 每次使用,把使用的节点放到链表最前面,返回值
    def get(self, key):
        if key in self.map:
            node = self.map[key]      # 取出这个key和它的值
            self.list.remove(node)
            self.list.append_front(node)    #把这个缓存放到最前面
            return node.value
        else:
            return -1

    #添加一个缓存
    def put(self, key, value):
        # 先判断key是否已经在缓存里:
        if key in self.map:     #是在缓存里:更新value,放入最前面
            node = self.map.get(key)
            self.list.remove(node)
            node.value = value
            self.list.append_front(node)
        else:
            node = Node(key, value)
            #缓存已经满了的话需要先删掉最后一个再往head增加新的:
            if self.list.size >= self.list.capacity-1:
                old_node = self.list.remove()
                self.map.pop(old_node.key)  #删除节点同时要删除本地映射

            self.list.append_front(node)
            self.map[key] = node

    def print(self):
        self.list.print()

# test:
if __name__ == '__main__':
    cache = LRUCache(2)
    cache.put(2, 2)
    cache.print()
    cache.put(1, 1)
    cache.print()
    cache.put(3, 3)  # 淘汰了(1,1)
    cache.print()
    print(cache.get(1))
    cache.print()
    print(cache.get(2))
    cache.print()
    print(cache.get(3))
    cache.print()

结果:

{2: 2}
{1: 1}=>{2: 2}
{3: 3}=>{1: 1}
1
{1: 1}=>{3: 3}
-1
{1: 1}=>{3: 3}
3
{3: 3}=>{1: 1}


LFU(最不经常使用)算法:

淘汰缓存时,把使用频率最小的淘汰

存在相同频率的情况:每个频率单独做一个双向链表,淘汰时把最低频率的head节点淘汰掉(FIFO)

#! -*- encoding=utf-8 -*-

from computer_principle.doublelinkedlist import DoubleLinkedList, Node

class LFUNode(Node):
    def __init__(self, key, value):
        self.freq = 0   # 新增一个freq属性
        super(LFUNode, self).__init__(key, value)   # 这是python2的写法。python3:super().__init__(key, value)


class LFUCache():
    def __init__(self, capacity):
        self.capacity = capacity
        self.map = {}  # 保存key和value的映射关系
        # key: 频率, value:频率对应的双向链表
        self.freq_map = {}
        self.list = DoubleLinkedList(self.capacity)
        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 = 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)       #freq_map里的key都是数字
                node = self.freq_map[min_freq].pop()    #默认pop是head
                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()
            node = self.freq_map[node.freq].append(node)
            self.size += 1

    def print(self):
        print('****************************')
        for k, v in self.freq_map.items():      # 字典的items方法:返回可遍历的(键, 值) 元组数组
            print('Freq = {}'.format(k))
            self.freq_map[k].print()
        print('****************************')
        print()

# test:
if __name__ == '__main__':
    cache = LFUCache(2)
    cache.put(1, 1)
    cache.print()
    cache.put(2, 2)
    cache.print()
    print(cache.get(1))
    cache.print()
    cache.put(3, 3)
    cache.print()
    print(cache.get(2))
    cache.print()
    print(cache.get(3))
    cache.print()
    cache.put(4, 4)
    cache.print()
    print(cache.get(1))
    cache.print()
    # print(cache.get(2))
    print(cache.get(3))
    cache.print()
    print(cache.get(4))
    cache.print()

结果:

****************************
Freq = 1
{1: 1}
****************************

****************************
Freq = 1
{1: 1}=>{2: 2}
****************************

1
****************************
Freq = 1
{2: 2}
Freq = 2
{1: 1}
****************************

****************************
Freq = 1
{3: 3}
Freq = 2
{1: 1}
****************************

-1
****************************
Freq = 1
{3: 3}
Freq = 2
{1: 1}
****************************

3
****************************
Freq = 2
{1: 1}=>{3: 3}
****************************

****************************
Freq = 2
{3: 3}
Freq = 1
{4: 4}
****************************

-1
****************************
Freq = 2
{3: 3}
Freq = 1
{4: 4}
****************************

3
****************************
Freq = 1
{4: 4}
Freq = 3
{3: 3}
****************************

4
****************************
Freq = 3
{3: 3}
Freq = 2
{4: 4}
****************************