缓存机制
在计算机中,我们发明了缓存(cache)技术来对频繁热点使用的数据进行读取,以大大提高数据读取效率。缓存机制广泛应用于CPU、数据库和浏览器等软件设计中,它们可以将跟用户的交互体验产生质的飞跃。但是,用作缓存空间的硬件极其昂贵,一般软件能使用的缓存空间都很小。这样就需要数据结构与算法去提升缓存空间的合理利用率,我们通常将缓存的设计模式规定为最近最少使用策略 LRU (Least Recently Used)。
LRU 设计思路
- 如果此数据之前已经被缓存在数组或者链表中了,遍历得到这个数据所属位置,并将其从原来的位置删除,然后再插入到数组或者链表的首位;
- 如果未在数组和链表中发现此数据,则将此结点插入到数组和链表的头部;
- 数组和链表达到自身最大缓存容量,删除末尾的数据。
LRU 数组实现
import random
class LRUCache:
def __init__(self, capacity: int = 10):
self._capacity = capacity
self._data = []
def __len__(self):
return len(self._data)
def __repr__(self):
return str(self._data)
def is_exist(self, value: object) -> int:
for index in range(0, len(self._data)):
if self._data[index] == value:
return index
return -1
def save(self, value):
index = self.is_exist(value)
if index != -1:
self._data.insert(0, self._data.pop(index))
else:
if len(self) >= self._capacity:
self._data.pop(len(self) - 1)
self._data.insert(0, value)
if __name__ == '__main__':
cache = LRUCache(5)
for n in range(20):
i = random.randint(0, 7)
cache.save(i)
print("cache.save(%d)" % i, cache)
终端输出的结果:
cache.save(2) [2]
cache.save(2) [2]
cache.save(2) [2]
cache.save(6) [6, 2]
cache.save(6) [6, 2]
cache.save(3) [3, 6, 2]
cache.save(7) [7, 3, 6, 2]
cache.save(4) [4, 7, 3, 6, 2]
cache.save(3) [3, 4, 7, 6, 2]
cache.save(3) [3, 4, 7, 6, 2]
cache.save(6) [6, 3, 4, 7, 2]
cache.save(0) [0, 6, 3, 4, 7, 2]
cache.save(5) [5, 0, 6, 3, 4, 7]
cache.save(7) [7, 5, 0, 6, 3, 4]
cache.save(6) [6, 7, 5, 0, 3, 4]
cache.save(2) [2, 6, 7, 5, 0, 3]
cache.save(7) [7, 2, 6, 5, 0, 3]
cache.save(3) [3, 7, 2, 6, 5, 0]
cache.save(5) [5, 3, 7, 2, 6, 0]
cache.save(4) [4, 5, 3, 7, 2, 6]
LRU 链表实现
import random
class ListNode(object):
def __init__(self, value, next=None):
self.value = value
self.next = next
class LRUCache:
def __init__(self, capacity: int = 10):
self.capacity = capacity
self.head = ListNode(None, None)
self.length = 0
def __len__(self):
return self.length
def __repr__(self):
values = []
pointer = self.head.next
while pointer:
values.append(str(pointer.value))
pointer = pointer.next
return '->'.join(values)
def is_exist(self, value: object) -> object:
pointer = self.head
while pointer.next:
if pointer.next.value == value:
return pointer
pointer = pointer.next
return pointer
def remove_tail(self):
pointer = self.head
prev = None
while pointer.next:
prev = pointer
pointer = pointer.next
prev.next = None
self.length -= 1
@staticmethod
def insert_to_head(head, node):
node.next = head.next
head.next = node
def save(self, value: object):
prev = self.is_exist(value)
if len(self) > 0 and prev.next is not None:
dest = prev.next
prev.next = dest.next
self.insert_to_head(self.head, dest)
else:
if self.length >= self.capacity:
self.remove_tail()
self.insert_to_head(self.head, ListNode(value))
self.length += 1
if __name__ == '__main__':
cache = LRUCache(5)
for _ in range(20):
value = random.randint(0, 8)
cache.save(value)
print("cache.save(%d),length(%d)" % (value, len(cache)), cache)
终端输出的结果:
cache.save(0),length(1) 0
cache.save(1),length(2) 1->0
cache.save(3),length(3) 3->1->0
cache.save(0),length(3) 0->3->1
cache.save(4),length(4) 4->0->3->1
cache.save(4),length(4) 4->0->3->1
cache.save(0),length(4) 0->4->3->1
cache.save(1),length(4) 1->0->4->3
cache.save(5),length(5) 5->1->0->4->3
cache.save(7),length(5) 7->5->1->0->4
cache.save(0),length(5) 0->7->5->1->4
cache.save(6),length(5) 6->0->7->5->1
cache.save(7),length(5) 7->6->0->5->1
cache.save(3),length(5) 3->7->6->0->5
cache.save(8),length(5) 8->3->7->6->0
cache.save(1),length(5) 1->8->3->7->6
cache.save(0),length(5) 0->1->8->3->7
cache.save(6),length(5) 6->0->1->8->3
cache.save(8),length(5) 8->6->0->1->3
cache.save(3),length(5) 3->8->6->0->1