为 LRU Cache 设计一个数据结构。它应该支持以下操作: get和set 。
get(key) – 如果键存在于缓存中,则获取键的值(将始终为正),否则返回 -1。
set(key, value) – 如果键不存在,则设置或插入值。当缓存达到其容量时,它应该在插入新项目之前使最近最少使用的项目无效。
例子:
// Let’s say we have a LRU cache of capacity 2.
LRUCache cache = new LRUCache(2);
cache.set(1, 10); // it will store a key (1) with value 10 in the cache.
cache.set(2, 20); // it will store a key (2) with value 20 in the cache.
cache.get(1); // returns 10
cache.set(3, 30); // evicts key 2 and store a key (3) with value 30 in the cache.
cache.get(2); // returns -1 (not found)
cache.set(4, 40); // evicts key 1 and store a key (4) with value 40 in the cache.
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 30
cache.get(4); // returns 40
问: Adobe、Hike 和更多公司。
解决方案:
1. 蛮力方法:
我们将保留一个节点数组,每个节点将包含以下信息:
C++
struct Node
{
int key;
int value;
// It shows the time at which the key is stored.
// We will use the timeStamp to find out the
// least recently used (LRU) node.
int timeStamp;
Node(int _key, int _value)
{
key = _key;
value = _value;
// currentTimeStamp from system
timeStamp = currentTimeStamp;
}
};
// This code is contributed by subham348
Java
class Node {
int key;
int value;
// it shows the time at which the key is stored.
// We will use the timeStamp to find out the
// least recently used (LRU) node.
int timeStamp;
public Node(int key, int value)
{
this.key = key;
this.value = value;
// currentTimeStamp from system
this.timeStamp = currentTimeStamp;
}
}
C++
#include
using namespace std;
class LRUCache{
public:
class node
{
public:
int key;
int value;
node * prev;
node * next;
node(int _key,int _value)
{
key = _key;
value = _value;
}
};
node* head = new node(-1, -1);
node* tail = new node(-1, -1);
int cap;
map m;
// Constructor for initializing the
// cache capacity with the given value.
LRUCache(int capacity)
{
cap = capacity;
head->next = tail;
tail->prev = head;
}
void addnode(node * temp)
{
node * dummy = head->next;
head->next = temp;
temp->prev = head;
temp->next = dummy;
dummy->prev = temp;
}
void deletenode(node * temp)
{
node * delnext = temp->next;
node * delprev = temp->prev;
delnext->prev = delprev;
delprev->next = delnext;
}
// This method works in O(1)
int get(int key)
{
if (m.find(key) != m.end())
{
node * res = m[key];
m.erase(key);
int ans = res->value;
deletenode(res);
addnode(res);
m[key] = head->next;
cout << "Got the value : " << ans
<< " for the key: " << key << "\n";
return ans;
}
cout << "Did not get any value for the key: "
<< key << "\n";
return -1;
}
// This method works in O(1)
void set(int key, int value)
{
cout << "Going to set the (key, value) : ("
<< key << ", " << value << ")" << "\n";
if (m.find(key) != m.end())
{
node * exist = m[key];
m.erase(key);
deletenode(exist);
}
if (m.size() == cap)
{
m.erase(tail->prev->key);
deletenode(tail->prev);
}
addnode(new node(key, value));
m[key] = head->next;
}
};
// Driver code
int main()
{
cout << "Going to test the LRU "
<< "Cache Implementation\n";
LRUCache * cache = new LRUCache(2);
// It will store a key (1) with value
// 10 in the cache.
cache->set(1, 10);
// It will store a key (1) with value 10 in the
// cache.
cache->set(2, 20);
cout << "Value for the key: 1 is "
<< cache->get(1) << "\n"; // returns 10
// Evicts key 2 and store a key (3) with
// value 30 in the cache.
cache->set(3, 30);
cout << "Value for the key: 2 is "
<< cache->get(2) << "\n"; // returns -1 (not found)
// Evicts key 1 and store a key (4) with
// value 40 in the cache.
cache->set(4, 40);
cout << "Value for the key: 1 is "
<< cache->get(1) << "\n"; // returns -1 (not found)
cout << "Value for the key: 3 is "
<< cache->get(3) << "\n"; // returns 30
cout << "Value for the key: 4 is "
<< cache->get(4) << "\n"; // return 40
return 0;
}
// This code is contributed by CoderSaty
Java
import java.util.HashMap;
class Node {
int key;
int value;
Node pre;
Node next;
public Node(int key, int value)
{
this.key = key;
this.value = value;
}
}
class LRUCache {
private HashMap map;
private int capacity, count;
private Node head, tail;
public LRUCache(int capacity)
{
this.capacity = capacity;
map = new HashMap<>();
head = new Node(0, 0);
tail = new Node(0, 0);
head.next = tail;
tail.pre = head;
head.pre = null;
tail.next = null;
count = 0;
}
public void deleteNode(Node node)
{
node.pre.next = node.next;
node.next.pre = node.pre;
}
public void addToHead(Node node)
{
node.next = head.next;
node.next.pre = node;
node.pre = head;
head.next = node;
}
// This method works in O(1)
public int get(int key)
{
if (map.get(key) != null) {
Node node = map.get(key);
int result = node.value;
deleteNode(node);
addToHead(node);
System.out.println("Got the value : " + result
+ " for the key: " + key);
return result;
}
System.out.println("Did not get any value"
+ " for the key: " + key);
return -1;
}
// This method works in O(1)
public void set(int key, int value)
{
System.out.println("Going to set the (key, "
+ "value) : (" + key + ", "
+ value + ")");
if (map.get(key) != null) {
Node node = map.get(key);
node.value = value;
deleteNode(node);
addToHead(node);
}
else {
Node node = new Node(key, value);
map.put(key, node);
if (count < capacity) {
count++;
addToHead(node);
}
else {
map.remove(tail.pre.key);
deleteNode(tail.pre);
addToHead(node);
}
}
}
}
public class TestLRUCache {
public static void main(String[] args)
{
System.out.println("Going to test the LRU "
+ " Cache Implementation");
LRUCache cache = new LRUCache(2);
// it will store a key (1) with value
// 10 in the cache.
cache.set(1, 10);
// it will store a key (1) with value 10 in the
// cache.
cache.set(2, 20);
System.out.println("Value for the key: 1 is "
+ cache.get(1)); // returns 10
// evicts key 2 and store a key (3) with
// value 30 in the cache.
cache.set(3, 30);
System.out.println(
"Value for the key: 2 is "
+ cache.get(2)); // returns -1 (not found)
// evicts key 1 and store a key (4) with
// value 40 in the cache.
cache.set(4, 40);
System.out.println(
"Value for the key: 1 is "
+ cache.get(1)); // returns -1 (not found)
System.out.println("Value for the key: 3 is "
+ cache.get(3)); // returns 30
System.out.println("Value for the key: 4 is "
+ cache.get(4)); // return 40
}
}
Python3
# Class for a Doubly LinkedList Node
class DLLNode:
def __init__(self, key, val):
self.val = val
self.key = key
self.prev = None
self.next = None
# LRU cache class
class LRUCache:
def __init__(self, capacity):
# capacity: capacity of cache
# Initialize all variable
self.capacity = capacity
self.map = {}
self.head = DLLNode(0, 0)
self.tail = DLLNode(0, 0)
self.head.next = self.tail
self.tail.prev = self.head
self.count = 0
def deleteNode(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def addToHead(self, node):
node.next = self.head.next
node.next.prev = node
node.prev = self.head
self.head.next = node
# This method works in O(1)
def get(self, key):
if key in self.map:
node = self.map[key]
result = node.val
self.deleteNode(node)
self.addToHead(node)
print('Got the value : {} for the key: {}'.format(result, key))
return result
print('Did not get any value for the key: {}'.format(key))
return -1
# This method works in O(1)
def set(self, key, value):
print('going to set the (key, value) : ( {}, {})'.format(key, value))
if key in self.map:
node = self.map[key]
node.val = value
self.deleteNode(node)
self.addToHead(node)
else:
node = DLLNode(key, value)
self.map[key] = node
if self.count < self.capacity:
self.count += 1
self.addToHead(node)
else:
del self.map[self.tail.prev.key]
self.deleteNode(self.tail.prev)
self.addToHead(node)
if __name__ == '__main__':
print('Going to test the LRU Cache Implementation')
cache = LRUCache(2)
# it will store a key (1) with value
# 10 in the cache.
cache.set(1, 10)
# it will store a key (1) with value 10 in the cache.
cache.set(2, 20)
print('Value for the key: 1 is {}'.format(cache.get(1))) # returns 10
# evicts key 2 and store a key (3) with
# value 30 in the cache.
cache.set(3, 30)
print('Value for the key: 2 is {}'.format(
cache.get(2))) # returns -1 (not found)
# evicts key 1 and store a key (4) with
# value 40 in the cache.
cache.set(4, 40)
print('Value for the key: 1 is {}'.format(
cache.get(1))) # returns -1 (not found)
print('Value for the key: 3 is {}'.format(cache.get(3))) # returns 30
print('Value for the key: 4 is {}'.format(cache.get(4))) # returns 40
Java
import java.util.LinkedHashMap;
import java.util.Map;
class LRUCache {
private LinkedHashMap map;
private final int CAPACITY;
public LRUCache(int capacity)
{
CAPACITY = capacity;
map = new LinkedHashMap(capacity, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry eldest)
{
return size() > CAPACITY;
}
};
}
// This method works in O(1)
public int get(int key)
{
System.out.println("Going to get the value " +
"for the key : " + key);
return map.getOrDefault(key, -1);
}
// This method works in O(1)
public void set(int key, int value)
{
System.out.println("Going to set the (key, " +
"value) : (" + key + ", " + value + ")");
map.put(key, value);
}
}
public class TestLRUCacheWithLinkedHashMap {
public static void main(String[] args)
{
System.out.println("Going to test the LRU "+
" Cache Implementation");
LRUCache cache = new LRUCache(2);
// it will store a key (1) with value
// 10 in the cache.
cache.set(1, 10);
// it will store a key (1) with value 10 in the cache.
cache.set(2, 20);
System.out.println("Value for the key: 1 is " +
cache.get(1)); // returns 10
// evicts key 2 and store a key (3) with
// value 30 in the cache.
cache.set(3, 30);
System.out.println("Value for the key: 2 is " +
cache.get(2)); // returns -1 (not found)
// evicts key 1 and store a key (4) with
// value 40 in the cache.
cache.set(4, 40);
System.out.println("Value for the key: 1 is " +
cache.get(1)); // returns -1 (not found)
System.out.println("Value for the key: 3 is " +
cache.get(3)); // returns 30
System.out.println("Value for the key: 4 is " +
cache.get(4)); // return 40
}
}
数组的大小将等于给定的缓存容量。
(a)对于get(int key):我们可以简单地遍历数组并将每个节点的键与给定的键进行比较,并返回存储在该键的节点中的值。如果我们没有找到任何这样的节点,只需返回 -1。
时间复杂度: O(n)
(b) For set(int key, int value):如果数组已满,我们必须从数组中删除一个节点。为了找到 LRU 节点,我们将遍历数组并找到具有最小 timeStamp 值的节点。我们将简单地在 LRU 节点的位置插入新节点(具有新的键和值)。
如果数组未满,我们可以简单地在数组中的最后一个当前索引处插入一个新节点。
时间复杂度: O(n)
2.优化方法:
解决这个问题的关键是使用双链表,它使我们能够快速移动节点。
LRU 缓存是键和双链接节点的哈希映射。哈希映射使 get() 的时间为 O(1)。双链接节点列表使节点添加/删除操作 O(1)。
使用双向链表和 HashMap 的代码:
C++
#include
using namespace std;
class LRUCache{
public:
class node
{
public:
int key;
int value;
node * prev;
node * next;
node(int _key,int _value)
{
key = _key;
value = _value;
}
};
node* head = new node(-1, -1);
node* tail = new node(-1, -1);
int cap;
map m;
// Constructor for initializing the
// cache capacity with the given value.
LRUCache(int capacity)
{
cap = capacity;
head->next = tail;
tail->prev = head;
}
void addnode(node * temp)
{
node * dummy = head->next;
head->next = temp;
temp->prev = head;
temp->next = dummy;
dummy->prev = temp;
}
void deletenode(node * temp)
{
node * delnext = temp->next;
node * delprev = temp->prev;
delnext->prev = delprev;
delprev->next = delnext;
}
// This method works in O(1)
int get(int key)
{
if (m.find(key) != m.end())
{
node * res = m[key];
m.erase(key);
int ans = res->value;
deletenode(res);
addnode(res);
m[key] = head->next;
cout << "Got the value : " << ans
<< " for the key: " << key << "\n";
return ans;
}
cout << "Did not get any value for the key: "
<< key << "\n";
return -1;
}
// This method works in O(1)
void set(int key, int value)
{
cout << "Going to set the (key, value) : ("
<< key << ", " << value << ")" << "\n";
if (m.find(key) != m.end())
{
node * exist = m[key];
m.erase(key);
deletenode(exist);
}
if (m.size() == cap)
{
m.erase(tail->prev->key);
deletenode(tail->prev);
}
addnode(new node(key, value));
m[key] = head->next;
}
};
// Driver code
int main()
{
cout << "Going to test the LRU "
<< "Cache Implementation\n";
LRUCache * cache = new LRUCache(2);
// It will store a key (1) with value
// 10 in the cache.
cache->set(1, 10);
// It will store a key (1) with value 10 in the
// cache.
cache->set(2, 20);
cout << "Value for the key: 1 is "
<< cache->get(1) << "\n"; // returns 10
// Evicts key 2 and store a key (3) with
// value 30 in the cache.
cache->set(3, 30);
cout << "Value for the key: 2 is "
<< cache->get(2) << "\n"; // returns -1 (not found)
// Evicts key 1 and store a key (4) with
// value 40 in the cache.
cache->set(4, 40);
cout << "Value for the key: 1 is "
<< cache->get(1) << "\n"; // returns -1 (not found)
cout << "Value for the key: 3 is "
<< cache->get(3) << "\n"; // returns 30
cout << "Value for the key: 4 is "
<< cache->get(4) << "\n"; // return 40
return 0;
}
// This code is contributed by CoderSaty
Java
import java.util.HashMap;
class Node {
int key;
int value;
Node pre;
Node next;
public Node(int key, int value)
{
this.key = key;
this.value = value;
}
}
class LRUCache {
private HashMap map;
private int capacity, count;
private Node head, tail;
public LRUCache(int capacity)
{
this.capacity = capacity;
map = new HashMap<>();
head = new Node(0, 0);
tail = new Node(0, 0);
head.next = tail;
tail.pre = head;
head.pre = null;
tail.next = null;
count = 0;
}
public void deleteNode(Node node)
{
node.pre.next = node.next;
node.next.pre = node.pre;
}
public void addToHead(Node node)
{
node.next = head.next;
node.next.pre = node;
node.pre = head;
head.next = node;
}
// This method works in O(1)
public int get(int key)
{
if (map.get(key) != null) {
Node node = map.get(key);
int result = node.value;
deleteNode(node);
addToHead(node);
System.out.println("Got the value : " + result
+ " for the key: " + key);
return result;
}
System.out.println("Did not get any value"
+ " for the key: " + key);
return -1;
}
// This method works in O(1)
public void set(int key, int value)
{
System.out.println("Going to set the (key, "
+ "value) : (" + key + ", "
+ value + ")");
if (map.get(key) != null) {
Node node = map.get(key);
node.value = value;
deleteNode(node);
addToHead(node);
}
else {
Node node = new Node(key, value);
map.put(key, node);
if (count < capacity) {
count++;
addToHead(node);
}
else {
map.remove(tail.pre.key);
deleteNode(tail.pre);
addToHead(node);
}
}
}
}
public class TestLRUCache {
public static void main(String[] args)
{
System.out.println("Going to test the LRU "
+ " Cache Implementation");
LRUCache cache = new LRUCache(2);
// it will store a key (1) with value
// 10 in the cache.
cache.set(1, 10);
// it will store a key (1) with value 10 in the
// cache.
cache.set(2, 20);
System.out.println("Value for the key: 1 is "
+ cache.get(1)); // returns 10
// evicts key 2 and store a key (3) with
// value 30 in the cache.
cache.set(3, 30);
System.out.println(
"Value for the key: 2 is "
+ cache.get(2)); // returns -1 (not found)
// evicts key 1 and store a key (4) with
// value 40 in the cache.
cache.set(4, 40);
System.out.println(
"Value for the key: 1 is "
+ cache.get(1)); // returns -1 (not found)
System.out.println("Value for the key: 3 is "
+ cache.get(3)); // returns 30
System.out.println("Value for the key: 4 is "
+ cache.get(4)); // return 40
}
}
蟒蛇3
# Class for a Doubly LinkedList Node
class DLLNode:
def __init__(self, key, val):
self.val = val
self.key = key
self.prev = None
self.next = None
# LRU cache class
class LRUCache:
def __init__(self, capacity):
# capacity: capacity of cache
# Initialize all variable
self.capacity = capacity
self.map = {}
self.head = DLLNode(0, 0)
self.tail = DLLNode(0, 0)
self.head.next = self.tail
self.tail.prev = self.head
self.count = 0
def deleteNode(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def addToHead(self, node):
node.next = self.head.next
node.next.prev = node
node.prev = self.head
self.head.next = node
# This method works in O(1)
def get(self, key):
if key in self.map:
node = self.map[key]
result = node.val
self.deleteNode(node)
self.addToHead(node)
print('Got the value : {} for the key: {}'.format(result, key))
return result
print('Did not get any value for the key: {}'.format(key))
return -1
# This method works in O(1)
def set(self, key, value):
print('going to set the (key, value) : ( {}, {})'.format(key, value))
if key in self.map:
node = self.map[key]
node.val = value
self.deleteNode(node)
self.addToHead(node)
else:
node = DLLNode(key, value)
self.map[key] = node
if self.count < self.capacity:
self.count += 1
self.addToHead(node)
else:
del self.map[self.tail.prev.key]
self.deleteNode(self.tail.prev)
self.addToHead(node)
if __name__ == '__main__':
print('Going to test the LRU Cache Implementation')
cache = LRUCache(2)
# it will store a key (1) with value
# 10 in the cache.
cache.set(1, 10)
# it will store a key (1) with value 10 in the cache.
cache.set(2, 20)
print('Value for the key: 1 is {}'.format(cache.get(1))) # returns 10
# evicts key 2 and store a key (3) with
# value 30 in the cache.
cache.set(3, 30)
print('Value for the key: 2 is {}'.format(
cache.get(2))) # returns -1 (not found)
# evicts key 1 and store a key (4) with
# value 40 in the cache.
cache.set(4, 40)
print('Value for the key: 1 is {}'.format(
cache.get(1))) # returns -1 (not found)
print('Value for the key: 3 is {}'.format(cache.get(3))) # returns 30
print('Value for the key: 4 is {}'.format(cache.get(4))) # returns 40
Going to test the LRU Cache Implementation
Going to set the (key, value) : (1, 10)
Going to set the (key, value) : (2, 20)
Got the value : 10 for the key: 1
Value for the key: 1 is 10
Going to set the (key, value) : (3, 30)
Did not get any value for the key: 2
Value for the key: 2 is -1
Going to set the (key, value) : (4, 40)
Did not get any value for the key: 1
Value for the key: 1 is -1
Got the value : 30 for the key: 3
Value for the key: 3 is 30
Got the value : 40 for the key: 4
Value for the key: 4 is 40
Java使用 LinkedHashMap 的另一种实现:
removeEldestEntry () 被覆盖以在大小超出容量时强制执行删除旧映射的策略。
Java
import java.util.LinkedHashMap;
import java.util.Map;
class LRUCache {
private LinkedHashMap map;
private final int CAPACITY;
public LRUCache(int capacity)
{
CAPACITY = capacity;
map = new LinkedHashMap(capacity, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry eldest)
{
return size() > CAPACITY;
}
};
}
// This method works in O(1)
public int get(int key)
{
System.out.println("Going to get the value " +
"for the key : " + key);
return map.getOrDefault(key, -1);
}
// This method works in O(1)
public void set(int key, int value)
{
System.out.println("Going to set the (key, " +
"value) : (" + key + ", " + value + ")");
map.put(key, value);
}
}
public class TestLRUCacheWithLinkedHashMap {
public static void main(String[] args)
{
System.out.println("Going to test the LRU "+
" Cache Implementation");
LRUCache cache = new LRUCache(2);
// it will store a key (1) with value
// 10 in the cache.
cache.set(1, 10);
// it will store a key (1) with value 10 in the cache.
cache.set(2, 20);
System.out.println("Value for the key: 1 is " +
cache.get(1)); // returns 10
// evicts key 2 and store a key (3) with
// value 30 in the cache.
cache.set(3, 30);
System.out.println("Value for the key: 2 is " +
cache.get(2)); // returns -1 (not found)
// evicts key 1 and store a key (4) with
// value 40 in the cache.
cache.set(4, 40);
System.out.println("Value for the key: 1 is " +
cache.get(1)); // returns -1 (not found)
System.out.println("Value for the key: 3 is " +
cache.get(3)); // returns 30
System.out.println("Value for the key: 4 is " +
cache.get(4)); // return 40
}
}
Going to test the LRU Cache Implementation
Going to set the (key, value) : (1, 10)
Going to set the (key, value) : (2, 20)
Going to get the value for the key : 1
Value for the key: 1 is 10
Going to set the (key, value) : (3, 30)
Going to get the value for the key : 2
Value for the key: 2 is -1
Going to set the (key, value) : (4, 40)
Going to get the value for the key : 1
Value for the key: 1 is -1
Going to get the value for the key : 3
Value for the key: 3 is 30
Going to get the value for the key : 4
Value for the key: 4 is 40
你可以在这里找到 C++ 代码
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。