题目描述:
LRU是 Least Recently Used 的缩写,指“最近最少使用”,LRU环存就是使用这种原理实现,简单地说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉。常用于页面置换算法,是虚拟页式存储管理中常用的算法。
思路:
使用两个数据结构实现一个LRU环存:
(1) 使用双向链表实现的队列,队列的最大容量为缓存的大小。在使用的过程中,把最近使用的页面移动到队列头,最近没有使用的页面将被放在队列尾的位置;
(2) 使用一个哈希表,把页号作为键,把缓存在队列中的结点的地址作为值;(不是很理解为什么要用一个哈希表,下面代码中未使用哈希表,仅是使用了一个双向的队列)
当引用一个页面时,如果
所需的页面在内存中,只需要把这个页对应的结点移动到队列的前面。如果
所需要的页面不在内存中,此时需要把这个页面加载到内存中。简单地说,就是将一个新结点添加到队列的前面(最近使用),并在哈希表中更新相应的结点地址。如果队列是满的,那么就从队列尾部移除一个结点(队尾表示距离现在久远),并将新结点添加到队列的前面(队头表示现在,队尾表示过去
)。
代码实现:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2020/1/22 16:53
# @Author : buu
# @Software: PyCharm
# @Blog :https://blog.csdn.net/weixin_44321080
from collections import deque
class LRU:
def __init__(self, cacheSize):
self.cacheSize = cacheSize # 要缓存的数据大小
self.queue = deque()
# self.hashSet = set()
def isQueueFull(self): # 判断缓存队列是否满
return len(self.queue) == self.cacheSize
def enQueue(self, pageNum): # 把页号为 pageNum 的页缓存到队列中,同时也添加到 Hash 表中
if self.isQueueFull(): # 如果队列满了,则需要删除队尾的缓存的页
# self.hashSet.remove(self.queue[-1])
self.queue.pop()
self.queue.appendleft(pageNum)
# self.hashSet.add(pageNum) # 把新缓存的结点同时添加到 hash 表中
def accessPage(self, pageNum):
"""
当访问某一个page的时候会调用此函数,对于访问的page有两种情况:
1. 如果page在缓存队列中,直接把这个结点移动到队首;
2. 如果page不在缓存队列中,则把这个page缓存到队首;
:param pageNum:
:return:
"""
if pageNum not in self.queue: # page不在缓存队列中,则把它缓存到队首
self.enQueue(pageNum)
elif pageNum != self.queue[0]: # page在缓存队列中,则移动到队首
self.queue.remove(pageNum)
self.queue.appendleft(pageNum)
def printQueue(self):
while len(self.queue) > 0:
print(self.queue.popleft(), end=' ')
if __name__ == '__main__':
lru = LRU(3) # 假设缓存大小为3
lru.accessPage(1) # 队列:1
lru.accessPage(2) # 队列:2,1
lru.accessPage(5) # 队列:5,2,1
lru.accessPage(1) # 队列:1,5,2,因为1在队列中了,所以移到队首
lru.accessPage(6) # 队列:6,1,5
lru.accessPage(7) # 队列:7,6,1
lru.printQueue() # 通过上面的访问序列后,打印缓存的信息
结果:
end