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 近期最少使用缓存的更多相关文章

  1. leetcode 146. LRU Cache 、460. LFU Cache

    LRU算法是首先淘汰最长时间未被使用的页面,而LFU是先淘汰一定时间内被访问次数最少的页面,如果存在使用频度相同的多个项目,则移除最近最少使用(Least Recently Used)的项目. LFU ...

  2. [LeetCode] 146. LRU Cache 最近最少使用页面置换缓存器

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  3. LeetCode之LRU Cache 最近最少使用算法 缓存设计

    设计并实现最近最久未使用(Least Recently Used)缓存. 题目描述: Design and implement a data structure for Least Recently ...

  4. leetcode 146. LRU Cache ----- java

    esign and implement a data structure for Least Recently Used (LRU) cache. It should support the foll ...

  5. Java for LeetCode 146 LRU Cache 【HARD】

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  6. leetcode@ [146] LRU Cache (TreeMap)

    https://leetcode.com/problems/lru-cache/ Design and implement a data structure for Least Recently Us ...

  7. Leetcode#146 LRU Cache

    原题地址 以前Leetcode的测试数据比较弱,单纯用链表做也能过,现在就不行了,大数据会超时.通常大家都是用map+双向链表做的. 我曾经尝试用C++的list容器来写,后来发现map没法保存lis ...

  8. LeetCode 146. LRU缓存机制(LRU Cache)

    题目描述 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 (k ...

  9. [Leetcode]146.LRU缓存机制

    Leetcode难题,题目为: 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key ...

随机推荐

  1. python dijkstra 最短路算法示意代码

    def dijkstra(graph, from_node, to_node): q, seen = [(0, from_node, [])], set() while q: cost, node, ...

  2. 微信小程序中,如果没有参数,如何设置默认参数?

    现在学会小程序,这方面的知识,需要积累. 现在的情况是这样: 如果想从后端获取产品列表,而这些列表是可以根据分类来获取的,也是可以获取所有产品的. 那么,为了不使小程序报错,那么,我们就可以将不传的参 ...

  3. sql查询时增加自动编号和分页

    查询时加序号 a:没有主键的情形: ,) as iid,* into #tmp from TableName Select * from #tmp Drop table #tmp b:有主键的情形: ...

  4. HDU3109: Worms(字符串变换类 DP)

    pro:开始有一个字母虫,然后字母虫在每一天可以选择自己身上的部分字母变换,变换规则形如A->BC. 现状给定最终字母虫的字符串,求最少用了多少天. 如有规则A->BC,B->AC, ...

  5. CLR如何将SEH异常映射到托管异常类型

    托管异常处理构建在Windows操作系统的结构化异常处理之上,通常称为SEH.这意味着CLR了解如何在SEH和托管异常系统之间进行互操作,这是一个非常关键的点,因为SEH基于异常代码的概念,而托管异常 ...

  6. vigil deb 包制作

    前边有写过简单rpm 包的制作,现在制作一个简单的deb 包. deb 包的制作是通过源码编译+ fpm 环境准备 rust curl https://sh.rustup.rs -sSf | sh 配 ...

  7. 「PKUSC2018」星际穿越

    传送门 Solution  倍增 Code  #include <bits/stdc++.h> #define reg register #define ll long long usin ...

  8. C++2.0新特性(七)——<Smart Pointer(智能指针)之weak_ptr>

    一.weak_ptr出现的意义 上一节提到过shared_ptr,它会自动释放“不再需要使用的对象”的相应的资源,但是它不是万能的,在某些时候(比如说循环引用),它会显得力不从心,这就是weak_pt ...

  9. 使用JS计算前一天和后一天

    使用JS实现前一天和后一天 首先,我们先在html中写两个按钮来进行简单操作 <button onclick="Before()">前一天</button> ...

  10. forEach, map, filter方法区别

    听说for循环已经成了菜鸟标配...? 瑟瑟发抖 赶紧找来资料补一补 1, forEach循环,循环数组中每一个元素并采取操作, 没有返回值, 可以不用知道数组长度 2, map函数,遍历数组每个元素 ...