替换策略以及 cache的写操作策略
- cache写入策略(
类似redis 和 数据库的关系。 先写入缓存,还是先写入数据库)
首先建立双向链表
- 要注意的是,双链表增加
头结点
,会很方便操作 - 还有双链表的插入删除操作
- 上代码:
功能
- 弹出头部节点(pop)
- 默认尾部添加结点(append),尾插法
- 往头部添加结点(append_front),头插法
- 删除节点(remove),不加参数时,删除尾部结点, 加蚕食时,删除指定结点
- 打印 字符串格式化的 链表(print)
class Node:
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None
def __str__(self):
val = '{%d: %d}' % (self.key, self.value)
return val
class DoubleLinkedList():
def __init__(self, capacity=0xffff):
"""
param: capacity: 链表容量
param: head: 头指针,指向头结点。 head.next == None。头结点的好处很多:
1. 使首元结点(第一个正式存放数据的节点)无序进行特殊处理,表示都是 xxx.next
2. 便于非空表和空表的统一处理。 判空为head.next == None。 不会存在头指针直接指向 None的情况
3. 头结点还可存储一些信息。
param: tail: 尾指针,表为空时,头尾指针都指向头结点
param: cur_size: 当前链表长度
"""
self.capacity = capacity
self.head = Node()
self.tail = self.head
self.cur_size = 0
def __add_head(self, node):
"""
头插法向链表中插入数据,分三种情况:
1. 为空链表
2. 不为空, 没达到最大容量
3. 达到最大容量
"""
if not self.head.next:
self.head.next = node
node.prev = self.head
self.tail = node
elif self.cur_size < self.capacity:
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
else:
pass
self.cur_size += 1
return node
def __add_tail(self, node):
"""
尾插法: 同样分三种情况
设置头结点的好处就体现出来了! 本来还要进行分情况讨论链表是否为空,
这里就不需要讨论了~ ,都进行统一处理。
"""
self.tail.next = node
node.prev = self.tail
self.tail = self.tail.next
self.cur_size +=1
node.next = None
return node
def __remove(self, node=None):
"""
删除任意节点:
1. 当不传入node时,默认尾部删除
2. node就是 尾部结点
3. 删除其他位置结点
"""
if not node or node == self.tail:
self.__del_tail()
else:
node.prev.next = node.next
node.next.prev = node.prev
self.cur_size -= 1
return node
def __del_head(self):
"""
删除首元结点
头结点使得处理逻辑都是一样的,直接调用 __remoove()方法
"""
return self.__remove(self.head.next)
def __del_tail(self):
""" 删除尾部结点 """
if self.head.next == None:
return
p = self.tail
self.tail.prev.next = None
self.tail = self.tail.prev
self.cur_size -= 1
e = self.tail
return p
def pop(self):
""" 弹出头部节点 """
return self.__del_head()
def append(self, node):
""" 默认尾部添加结点 """
return self.__add_tail(node)
def append_front(self, node):
""" 往头部添加结点 """
return self.__add_head(node)
def remove(self, node = None):
""" 删除节点 """
return self.__remove(node)
def print(self):
""" 打印当前链表 """
p = self.head
line = ''
while p.next:
line += '%s' % p.next
line += ' ==> '
p = p.next
print(line)
if __name__ == '__main__':
l = DoubleLinkedList(10)
nodes = []
for i in range(10):
node = Node(i, i)
nodes.append(node)
l.append(nodes[0])
l.print()
l.append(nodes[1])
l.print()
l.pop()
l.print()
l.append(nodes[2])
l.print()
l.append_front(nodes[3])
l.print()
l.append(nodes[4])
l.print()
l.remove(nodes[2])
l.print()
l.remove()
l.print()
测试结果的正确性:运行结果如下
[root@localhost 计算机组成]
{0: 0} ==>
{0: 0} ==> {1: 1} ==>
{1: 1} ==>
{1: 1} ==> {2: 2} ==>
{3: 3} ==> {1: 1} ==> {2: 2} ==>
{3: 3} ==> {1: 1} ==> {2: 2} ==> {4: 4} ==>
{3: 3} ==> {1: 1} ==> {4: 4} ==>
{3: 3} ==> {1: 1} ==>
缓存置换算法实现
- 下列代码实现只是考虑了缓存替换,
并没有考虑主存的相关操作
。
(一)FIFO(先进先出算法)
from DoubleLinkedList import DoubleLinkedList, Node
class FIFOCache(object):
def __init__(self, capacity):
"""
param: capacity: 也是用来设置容量大小的
param: map: 定义映射表。加快链表元素的查找速度。
key就是 链表key,value就是 node节点本身的引用
param: list: 双向链表对象
param: list: 当前元素个数
"""
self.capacity = capacity
self.map = {}
self.list = DoubleLinkedList(self.capacity)
self.size = 0
def get(self, key):
""" 实现get方法
找到返回value, 找不到返回 -1
"""
node = self.map.get(key, None)
if not node: return -1
return node.value
def put(self, key, value):
""" 实现put方法
if 如果已存在:
则1.更新该节点对应的value,2.将该节点从原位置删除,3.将节点添加到尾部
(好处是,少了重复创建结点)
else:不存在就判断是否插入新的
if 容量已满:
需要删除(头部节点, 删除map中的映射)
添加
"""
node = self.map.get(key, None)
if node:
self.list.remove(node)
node.value = value
self.list.append(node)
else:
if self.size == self.capacity:
node = self.list.pop()
del self.map[node.key]
self.size -= 1
node = Node(key, value)
self.list.append(node)
self.map[key] = node
self.size += 1
def print(self):
self.list.print()
if __name__ == '__main__':
cache = FIFOCache(2)
cache.put(1, 1)
cache.print()
cache.put(2, 2)
cache.print()
print(cache.get(1))
cache.put(3, 3)
cache.print()
print(cache.get(2))
cache.print()
cache.put(4, 4)
cache.print()
print(cache.get(1))
[root@localhost 计算机组成]
{1: 1} ==>
{1: 1} ==> {2: 2} ==>
1
{2: 2} ==> {3: 3} ==>
2
{2: 2} ==> {3: 3} ==>
{3: 3} ==> {4: 4} ==>
-1
(二)LRU(最近最少使用算法)
from DoubleLinkedList import DoubleLinkedList, Node
class LRUCache():
def __init__(self, capacity):
self.capacity = capacity
self.map = {}
self.list = DoubleLinkedList(self.capacity)
self.size = 0
def get(self, key):
"""
LRU算法,当在链表中时,不仅缓存,还要将缓存置于链表头部
"""
node = self.map.get(key, None)
if not node: return -1
self.list.remove(node)
self.list.append_front(node)
return node.value
def put(self, key, value):
"""
if 已经存在该结点:
(实际上就是更新cache了)
1. 删除原来在的位置
2. 更新值value
3. 重新插入到链表头部
else:
if 缓存已经满了(达到长度)
删除最后一个元素(最近最少使用的结点)
表头加入新元素
else:
未满,直接表头加元素
"""
node = self.map.get(key, None)
if node:
self.list.remove(node)
node.value = value
self.list.append_front(node)
else:
node = Node(key, value)
if self.size == self.capacity:
old_node = self.list.remove()
self.map.pop(old_node.key)
self.size -= 1
self.list.append_front(node)
self.map[key] = node
self.size += 1
def print(self):
self.list.print()
if __name__ == '__main__':
cache = LRUCache(2)
cache.put(2, 2)
cache.print()
cache.put(1, 1)
cache.print()
cache.put(3, 3)
cache.print()
print(cache.get(1))
cache.print()
print(cache.get(2))
cache.print()
print(cache.get(3))
cache.print()
[root@localhost 计算机组成]
{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(最不经常使用算法)
from DoubleLinkedList import DoubleLinkedList, Node
class LFUNode(Node):
""" 继承Node节点类,新加属性freq表示频率 """
def __init__(self, key, value) -> None:
super(LFUNode, self).__init__(key, value)
self.freq = 0
class LFUCache():
def __init__(self, capacity):
"""
param: map: 依然保存所有节点的映射,方便查询
param: freq_map: 字典类型。
key: 频率, value:频率对应的双链表。
当发生淘汰时:
1. 选择频率最低的链表
2. 选择从链表头部删除(因为尾部添加元素, 头部的元素在时间上,也是最久未被访问的)
param: size: 当前节点个数。
没有一个链表存储所有节点。 而是把节点都分开,按照频率,存放在每个小的链表里
param: capacity: 总容量
"""
self.capacity = capacity
self.map = {}
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].cur_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):
node = self.map.get(key, None)
if not node: return -1
self.__update_freq(node)
return node.value
def put(self, key, value):
node = self.map.get(key, None)
if node:
node.value = value
self.__update_freq(node)
else:
if self.size >= self.capacity:
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()
node = self.freq_map[node.freq].append(node)
self.size += 1
def print(self):
print('-'*50)
for k, v in self.freq_map.items():
print('freq = %d' % k)
v.print()
print('$'*50)
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(3))
cache.print()
print(cache.get(4))
cache.print()
[root@localhost 计算机组成]
--------------------------------------------------
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} ==>
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$