[LeetCode] 146. LRU Cache 近期最少使用缓存
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(Least Recently Used),实现get(key), set(key, value)功能。
get(key):取值(key恒为正), 不存在时返回-1。如果存在,返回值,并且delete此key,在从新写入cache,因为要最近刚使用过,要把它放到队尾。
set(key, value):缓存已满,删除近期最久未被使用的节点,添加新节点进缓存。缓存未满,节点存在,修改value;节点不存在,添加新节点进缓存;最后更新此节点到队尾。
解法1: 双向链表(Doubly-Linked List) + HashMap
双向链表:维护缓存节点CacheNode,凡是被访问(新建/修改命中/访问命中)过的节点,一律在访问完成后移动到双向链表尾部,保证链表尾部始终为最新节点;保证链表头部始终为最旧节点,LRU策略删除时表现为删除双向链表头部;由于链表不支持随机访问,使用HashMap+双向链表实现LRU缓存,HashMap中键值对:<key, CacheNode>。
解法2: OrderedDict有序字典
Time: Get O(1) Set O(1), Space: O(N)
Java:
import java.util.HashMap; class Solution {
private HashMap<Integer, CacheNode> map;
private int capacity;
// head.next和tail.next指向链表头尾,包起来防止null
private CacheNode head = new CacheNode(-1, -1);
private CacheNode tail = new CacheNode(-1, -1); private class CacheNode {
int key, value;
CacheNode pre, next;
CacheNode(int key, int value) {
this.key = key;
this.value = value;
this.pre = null;
this.next = null;
}
} public Solution(int capacity) {
this.map = new HashMap<>();
this.capacity = capacity;
} // 将已有节点或新建节点移动到链表尾部
private void moveToTail(CacheNode target, boolean isNew) {
// 尾部节点显然不需要移动
if (target != tail.next) {
if (!isNew) {
// 修改旧节点的双向链表指针
target.pre.next = target.next;
target.next.pre = target.pre;
}
// 添加节点到链表尾部
tail.next.next = target;
target.pre = tail.next;
tail.next = target;
}
} // 命中节点添加到链表尾部,未命中返回-1
public int get(int key) {
if (map.containsKey(key)) {
CacheNode target = map.get(key);
// 将已有节点移动到链表尾部
moveToTail(target, false);
// 此时链表尾部tail.next = target,更新next指向null,防止出现环
tail.next.next = null;
return target.value;
}
return -1;
} public void set(int key, int value) {
if (map.containsKey(key)) {
CacheNode target = map.get(key);
target.value = value;
map.put(key, target);
// 将访问过的已有节点移动到链表尾部
moveToTail(target, false);
} else if(map.size() < capacity) { // cache未满,添加节点
CacheNode newNode = new CacheNode(key, value);
map.put(key, newNode);
if (head.next == null) {
head.next = newNode;
newNode.pre = head;
tail.next = newNode;
} else {
// 将新建节点移动到链表尾部
moveToTail(newNode, true);
}
} else { // cache已满,淘汰链表链表头部节点,新节点加入到链表尾部
CacheNode newNode = new CacheNode(key, value);
map.remove(head.next.key);
map.put(key, newNode);
// cache中只有一个元素
if (head.next == tail.next) {
head.next = newNode;
tail.next = newNode;
} else { // cache中不止一个元素,删除头部
head.next.next.pre = head; // 更新新头部.pre = head
head.next = head.next.next;// 更新新头部
// 将新建节点移动到链表尾部
moveToTail(newNode, true);
}
}
}
}
Python:
class Node:
def __init__(self, key, val):
self.key = key
self.val = val
self.prev = None
self.next = None class LRUCache:
# @param capacity, an integer
def __init__(self, capacity):
self.capacity = capacity
self.size = 0
self.dummyNode = Node(-1, -1)
self.tail = self.dummyNode
self.entryFinder = {} # @return an integer
def get(self, key):
entry = self.entryFinder.get(key)
if entry is None:
return -1
else:
self.renew(entry)
return entry.val # @param key, an integer
# @param value, an integer
# @return nothing
def set(self, key, value):
entry = self.entryFinder.get(key)
if entry is None:
entry = Node(key, value)
self.entryFinder[key] = entry
self.tail.next = entry
entry.prev = self.tail
self.tail = entry
if self.size < self.capacity:
self.size += 1
else:
headNode = self.dummyNode.next
if headNode is not None:
self.dummyNode.next = headNode.next
headNode.next.prev = self.dummyNode
del self.entryFinder[headNode.key]
else:
entry.val = value
self.renew(entry) def renew(self, entry):
if self.tail != entry:
prevNode = entry.prev
nextNode = entry.next
prevNode.next = nextNode
nextNode.prev = prevNode
entry.next = None
self.tail.next = entry
entry.prev = self.tail
self.tail = entry
Python:
class ListNode(object):
def __init__(self, key, val):
self.val = val
self.key = key
self.next = None
self.prev = None class LinkedList(object):
def __init__(self):
self.head = None
self.tail = None def insert(self, node):
node.next, node.prev = None, None # avoid dirty node
if self.head is None:
self.head = node
else:
self.tail.next = node
node.prev = self.tail
self.tail = node def delete(self, node):
if node.prev:
node.prev.next = node.next
else:
self.head = node.next
if node.next:
node.next.prev = node.prev
else:
self.tail = node.prev
node.next, node.prev = None, None # make node clean class LRUCache(object): def __init__(self, capacity):
self.list = LinkedList()
self.dict = {}
self.capacity = capacity def _insert(self, key, val):
node = ListNode(key, val)
self.list.insert(node)
self.dict[key] = node def get(self, key):
if key in self.dict:
val = self.dict[key].val
self.list.delete(self.dict[key])
self._insert(key, val)
return val
return -1 def set(self, key, val):
if key in self.dict:
self.list.delete(self.dict[key])
elif len(self.dict) == self.capacity:
del self.dict[self.list.head.key]
self.list.delete(self.list.head)
self._insert(key, val)
Python:
class LRUCache: def __init__(self, capacity):
self.capacity = capacity
self.cache = collections.OrderedDict() def get(self, key):
if not key in self.cache:
return -1
value = self.cache.pop(key)
self.cache[key] = value
return value def set(self, key, value):
if key in self.cache:
self.cache.pop(key)
elif len(self.cache) == self.capacity:
self.cache.popitem(last=False)
self.cache[key] = value
C++:
class LRUCache{
public:
LRUCache(int capacity) {
cap = capacity;
} int get(int key) {
auto it = m.find(key);
if (it == m.end()) return -1;
l.splice(l.begin(), l, it->second);
return it->second->second;
} void set(int key, int value) {
auto it = m.find(key);
if (it != m.end()) l.erase(it->second);
l.push_front(make_pair(key, value));
m[key] = l.begin();
if (m.size() > cap) {
int k = l.rbegin()->first;
l.pop_back();
m.erase(k);
}
} private:
int cap;
list<pair<int, int> > l;
unordered_map<int, list<pair<int, int> >::iterator> m;
};
All LeetCode Questions List 题目汇总
[LeetCode] 146. LRU Cache 近期最少使用缓存的更多相关文章
- leetcode 146. LRU Cache 、460. LFU Cache
LRU算法是首先淘汰最长时间未被使用的页面,而LFU是先淘汰一定时间内被访问次数最少的页面,如果存在使用频度相同的多个项目,则移除最近最少使用(Least Recently Used)的项目. LFU ...
- [LeetCode] 146. LRU Cache 最近最少使用页面置换缓存器
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- LeetCode之LRU Cache 最近最少使用算法 缓存设计
设计并实现最近最久未使用(Least Recently Used)缓存. 题目描述: Design and implement a data structure for Least Recently ...
- leetcode 146. LRU Cache ----- java
esign and implement a data structure for Least Recently Used (LRU) cache. It should support the foll ...
- Java for LeetCode 146 LRU Cache 【HARD】
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- leetcode@ [146] LRU Cache (TreeMap)
https://leetcode.com/problems/lru-cache/ Design and implement a data structure for Least Recently Us ...
- Leetcode#146 LRU Cache
原题地址 以前Leetcode的测试数据比较弱,单纯用链表做也能过,现在就不行了,大数据会超时.通常大家都是用map+双向链表做的. 我曾经尝试用C++的list容器来写,后来发现map没法保存lis ...
- LeetCode 146. LRU缓存机制(LRU Cache)
题目描述 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 (k ...
- [Leetcode]146.LRU缓存机制
Leetcode难题,题目为: 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key ...
随机推荐
- 自定义控件LengthValidator
1.创建自定义验证控件:新建LengthValidator类并继承BaseValidator using System; using System.Collections.Generic; using ...
- 五大开源 Web 代理服务器横评:Squid、Privoxy、Varnish、Polipo、Tinyproxy
https://linux.cn/article-7119-1.html Web 代理软件转发 HTTP 请求时并不会改变数据流量.它们可以配置成透明代理,而无需客户端配置.它们还可以作为反向代理放在 ...
- c#中的多态学习总结
c#的多台方法,大体上和c++的类似,但是有点区别的,我这里刚刚初学,因此把重点记录下. 多态是同一个行为具有多个不同表现形式或形态的能力. 多态性意味着有多重形式.在面向对象编程范式中,多态性往往表 ...
- 微信小程序——选择某个区间的数字
很久没有更新文章啦~~记录下今天弄的一个小功能. 先上图: 需求很简单: 第1列改变的时候,第2列也随着改变,并且比第1列大1k. 这里用到了微信的picker 组件,对于不太熟练这个组件的小伙伴可以 ...
- Optimal Marks SPOJ - OPTM(最小割)
传送门 论文<最小割模型在信息学竞赛中的应用>原题 二进制不同位上互不影响,那么就按位跑网络流 每一位上,确定的点值为1的与S连一条容量为INF的有向边.为0的与T连一条容量为INF的有向 ...
- pgloader 方便的数据迁移工具
pgloader 是一个支持多种数据源迁移到pg 数据库的工具,高性能,使用灵活同时作者 也提供了docker 版本的镜像,今年3月份使用此工具的时候,发现好久都没更新了,但是 最近作者有了新版本的发 ...
- Html结构标准
< ! doctype html> 声明文档类型 <html> 根标签 <head> 头标签 <title></title> 标题标签 &l ...
- 29-ESP8266 SDK开发基础入门篇--编写TCP 客户端程序(Lwip RAW模式,非RTOS版,精简入门)
https://www.cnblogs.com/yangfengwu/p/11456667.html 由于上一节的源码长时间以后会自动断开,所以再做这一版非RTOS版的,咱直接用lua源码里面别人写的 ...
- 用Python操作MySQL(pymysql)
用python来操作MySQL,首先需要安装PyMySQL库(pip install pymysql). 连接MySQL: import pymysql connect=pymysql.connect ...
- mysql 存储过程 REPEAT ... UNTIL ... END REPEAT
begin declare b int; declare use_no varchar(10); declare use_name varchar(400); decla ...