CrazyDailyQuestion icon indicating copy to clipboard operation
CrazyDailyQuestion copied to clipboard

怎样实现一个LRU(最少使用算法)

Open MicroKibaco opened this issue 4 years ago • 0 comments

LRU Cache

LRU是Least Recent Used的缩写,即最少使用的Cache置换算法,是为虚拟页式存储管理服务的。Cache是高速缓存,这是IT行业经常见到的概念。CPU中的Cache能极大提高存取指令和数据的时间,让整个存储器(Cache+内存)既有Cache的高速度,又有内存的大容量。

Cache虽然速度快,但是容量有限。因此当Cache容量用完而又有新的内容添加进来的时候,就需要选取Cache中的部分内容进行舍弃,然后添加新的内容。LRU Cache的替换规则是:每次选取最久不被使用的内容进行舍弃,然后添加新的内容。之前上操作系统,老师告诉我的LRU Cache Algorithm中文翻译是最近最久未使用算法。

思路

LRU的典型实现是double linked list + hash map。原理是: 双向链表根据每个节点最近被访问的时间有序存储,最近被访问的节点存储在表头,最近没有被访问的节点存储的表尾,存储依据是因为:最近被访问的节点在接下来的一段时间仍有很大的概率被再次访问到。 哈希表的作用是用来提高查找效率,如果不使用哈希表,则查找一个节点的时间复杂度是O(n),而使用了哈希表,则每个节点的查找时间复杂度为O(1)。

查找(GET)操作:

根据键值查找hashmap,如果没找到直接返回-1 若找到对应节点node,则将其插入到双向链表表头 返回node的value值

插入(SET)操作:

根据键值查找hashmap。如果找到,则直接将该节点移到表头即可 如果没有找到,首先判断当前Cache是否已满 如果已满,则删除表尾节点 将新节点插入到表头

AC代码

AC代码如下,写的比较乱,其实很多方法是可以简洁复用的,但是这里之所以不改,是为了让大家更好的清楚LRU的流程和原理:
public class LRUCache {
	private HashMap<Integer, DoubleListNode> mHashMap;
	private DoubleListNode head;
	private DoubleListNode tail;
	private int capacity;
	private int currentsize;
 
	public LRUCache(int capacity) {
		this.capacity = capacity;
		this.currentsize = 0;
		this.mHashMap = new HashMap<Integer, DoubleListNode>();
		this.head = this.tail = null;
	}
 
	public int get(int key) {
		if (mHashMap.containsKey(key)) {
			DoubleListNode tNode = mHashMap.get(key);
			if (tNode == tail) {
				if (currentsize > 1) {
					removeNodeFromTail();
					moveNodeToHead(tNode);
				}
			} else if (tNode == head) {
				// do nothing
			} else {
				tNode.pre.next = tNode.next;
				tNode.next.pre = tNode.pre;
				moveNodeToHead(tNode);
			}
			return mHashMap.get(key).value;
		} else {
			return -1;
		}
	}
 
	private void removeNodeFromTail() {
		tail = tail.pre;
		if (tail != null) {
			tail.next = null;
		}
	}
 
	private void moveNodeToHead(DoubleListNode node) {
		head.pre = node;
		node.next = head;
		node.pre = null;
		head = node;
	}
 
	public void set(int key, int value) {
		if (mHashMap.containsKey(key)) {
			// 更新HashMap中对应的值,并将key对应的Node移至队头
			DoubleListNode tNode = mHashMap.get(key);
			tNode.value = value;
			if (tNode == tail) {
				if (currentsize > 1) {
					removeNodeFromTail();
					moveNodeToHead(tNode);
				}
			} else if (tNode == head) {
				// do nothing
			} else {
				tNode.pre.next = tNode.next;
				tNode.next.pre = tNode.pre;
				moveNodeToHead(tNode);
			}
 
			mHashMap.put(key, tNode);
		} else {
			DoubleListNode node = new DoubleListNode(key, value);
			mHashMap.put(key, node);
			if (currentsize == 0) {
				head = tail = node;
				currentsize += 1;
			} else if (currentsize < capacity) {
				moveNodeToHead(node);
				currentsize += 1;
			} else {
				// 删除tail节点,并且增加一个head节点
				mHashMap.remove(tail.key);
				removeNodeFromTail();
 
				// 增加头节点
				moveNodeToHead(node);
			}
		}
	}
 
	public static void main(String[] args) {
		LRUCache lruCache = new LRUCache(1);
		lruCache.set(2, 1);
		System.out.println(lruCache.get(2));
		lruCache.set(3, 2);
		System.out.println(lruCache.get(2));
		System.out.println(lruCache.get(3));
	}
 
	private static class DoubleListNode {
		public DoubleListNode pre;
		public DoubleListNode next;
		public int key;
		public int value;
 
		public DoubleListNode(int key, int value) {
			this.key = key;
			this.value = value;
			this.pre = this.next = null;
		}
	}
 
}

MicroKibaco avatar Nov 22 '20 22:11 MicroKibaco