LRU算法&&LeetCode解题报告

题目

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

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。原理是:
  1. 双向链表根据每个节点最近被访问的时间有序存储,最近被访问的节点存储在表头,最近没有被访问的节点存储的表尾,存储依据是因为:最近被访问的节点在接下来的一段时间仍有很大的概率被再次访问到。
  2. 哈希表的作用是用来提高查找效率,如果不使用哈希表,则查找一个节点的时间复杂度是O(n),而使用了哈希表,则每个节点的查找时间复杂度为O(1)。
    查找(GET)操作:
  • 根据键值查找hashmap,如果没找到直接返回-1
  • 若找到对应节点node,则将其插入到双向链表表头
  • 返回node的value值
    插入(SET)操作:
  • 根据键值查找hashmap。如果找到,则直接将该节点移到表头即可
  • 如果没有找到,首先判断当前Cache是否已满
  • 如果已满,则删除表尾节点
  • 将新节点插入到表头

AC代码

    AC代码如下,写的比较乱,其实很多方法是可以简洁复用的,但是这里之所以不改,是为了让大家更好的清楚LRU的流程和原理:
import java.util.HashMap;

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;
		}
	}

}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
FIFO算法LRU算法都是为了解决缓存淘汰问题而设计的。FIFO算法是一种按照元素插入顺序排序的数据结构,元素在数据结构中按照先进先出的顺序进行存储和访问。在实现缓存时,可以根据实际情况选择使用FIFO算法LRU算法,以达到最佳的缓存淘汰效果。 LRU(Least Recently Used)算法是一种常见的缓存淘汰算法,它的基本思想是“最近最少使用”。在缓存中,最近使用的数据会被优先淘汰,而最近未使用的数据则需要被淘汰以腾出缓存空间。为了实现这一功能,LRU算法通过定期淘汰最近最少使用的数据来保持缓存的最佳状态。LRU算法的核心思想是使用一个缓存数组,数组中的每个元素都存储了一个键值对。每个键值对表示缓存中的一个数据项,包含数据项的键和值。 FIFO算法LRU算法的区别在于,FIFO算法是按照元素插入顺序排序的,而LRU算法是按照元素最近使用的时间排序的。因此,当缓存中的数据项被频繁访问时,LRU算法的效果会更好,因为它会优先保留最近使用的数据项,而FIFO算法则无法做到这一点。 以下是一个使用Python实现LRU算法的例子: ```python from collections import OrderedDict class LRUCache: def __init__(self, capacity: int): self.cache = OrderedDict() self.capacity = capacity def get(self, key: int) -> int: if key not in self.cache: return -1 self.cache.move_to_end(key) return self.cache[key] def put(self, key: int, value: int) -> None: if key in self.cache: self.cache.move_to_end(key) self.cache[key] = value if len(self.cache) > self.capacity: self.cache.popitem(last=False) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值