146. LRU Cache
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get
and put
.
get(key)
- Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.put(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.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
LRUCache cache = new LRUCache( 2 /* capacity */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.put(4, 4); // evicts key 1
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4
/**************************************************************************
* file name 146.CRUCache.cpp
*
* 146. [LRU Cache](https://leetcode.com/problems/lru-cache/)
*
* Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.
* Implement the LRUCache class:
*
* LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
* int get(int key) Return the value of the key if the key exists, otherwise return -1.
* void put(int key, int value) Update the value of the key if the key exists.
* Otherwise, add the key-value pair to the cache.
* If the number of keys exceeds the capacity from this operation, evict the least recently used key.
* The functions get and put must each run in O(1) average time complexity.
**************************************************************************/
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
/// C++ solution
#include <iostream>
#include <list>
#include <unordered_map>
using namespace std;
class _LRUCache {
private:
int m_capacity;
struct CacheNode {
int key;
int value;
CacheNode(int k, int v) :key(k), value(v) {}
};
list<CacheNode> m_cacheList;
unordered_map<int, list<CacheNode>::iterator> m_cacheMap;// [key - CacheNode] hash
public:
_LRUCache(int capacity) {
this->m_capacity = capacity;
}
int get(int key) {
if (m_cacheMap.find(key) == m_cacheMap.end()) return -1;
//move the node to the head of the list
m_cacheList.splice(m_cacheList.begin(), m_cacheList, m_cacheMap[key]); //move m_cacheMap[key] to begin
//update the hash map
m_cacheMap[key] = m_cacheList.begin();
return m_cacheMap[key]->value;
}
void set(int key, int value) {
if (m_cacheMap.find(key) == m_cacheMap.end()) {
//LRU Cache is full
if (m_cacheList.size() == m_capacity) {
m_cacheMap.erase(m_cacheList.back().key);
m_cacheList.pop_back();
}
m_cacheList.push_front(CacheNode(key, value));
m_cacheMap[key] = m_cacheList.begin();
}
else {
//if find the key, modify its value, and move the list node to the head of the list
m_cacheMap[key]->value = value;
m_cacheList.splice(m_cacheList.begin(), m_cacheList, m_cacheMap[key]);
m_cacheMap[key] = m_cacheList.begin();
}
}
// for test code
int size() {
return m_cacheList.size();
}
int capacity() {
return m_capacity;
}
void printList() {
bool once = true;
for (auto i : m_cacheList) {
if (once) {
once = false;
cout <<"["<< i.key << "," << i.value<<"]";
}
else {
cout << "->" << "[" << i.key << "," << i.value << "]";
}
}
cout << endl;
}
};
static void test3();
static void test4();
static int cpp() {
test3();
test4();
return 0;
}
static void test3() {
int size = 8;
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
//cout << "Set Capacity:\n";
//cin >> size;
_LRUCache A(size);
cout << "Capacity of Cache:" << A.capacity() << endl;
//set test
for (int i = 0; i != 2*size; i++) {
cout << "set:" << i << " ";
A.set(i, i);
A.printList();
}
cout << "\nRandom set\n";
srand(time(nullptr));
for (int i = 0; i != 3*size; i++) {
int k = (rand() % size);
int j = (rand() % size);
cout << "set(" << k << ":"<<j<<"):";
A.set(k, j);
A.printList();
}
cout << "\nRandom get\n";
srand(time(nullptr));
for (int i = 0; i != size*3; i++) {
int k = (rand() % (size*2));
cout << "get:" << k << " ";
if(A.get(k)!=-1)
A.printList();
else
cout << "cannot Get " << k << "\n";
}
cout << "========================\n\n";
}
static void test4(){
_LRUCache A(5);
A.set(1, 1);
A.set(2, 2);
A.set(3, 3);
A.set(4, 4);
A.printList();
A.set(5, 5);
A.printList();
A.set(6, 6);
A.set(7, 7);
A.set(8, 8);
A.printList();
A.set(9, 9);
A.set(0, 0);
A.printList();
cout << "========================\n\n";
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
/// C solution
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#define HEAD next /* first node in list */
#define TAIL previous /* last node in list */
#define HASH_SIZE 3179 /* hash table size */
typedef struct node { /* node of a linked list. */
struct node* next; /* Points at the next node in the list */
struct node* previous; /* Points at the previous node in the list */
} NODE;
/* data struct in double linked list */
typedef struct __DATA__ {
NODE node;
int key;
int value;
} DATA;
/* using Separate Chaining */
/* https://www.geeksforgeeks.org/hashing-set-2-separate-chaining */
typedef struct _HASH_ {
DATA *data;
struct _HASH_ *next; /* using linked list of records that have same hash function(int slot = key%HASH_SIZE) value. */
} HashElement;
typedef struct _LRU_ {
NODE *list;
int capacity;
int size;
HashElement *hashTable;
} LRUCache;
/* initializes a list descriptor */
void lstInit(NODE* list){
list->HEAD = list;
list->TAIL = list;
}
/* find first node in list */
NODE* lstFirst(NODE* list){
return (list->HEAD);
}
/* find last node in a list */
NODE* lstLast(NODE* list){
return (list->TAIL);
}
/* get the list empty status */
int lstEmpty(NODE* list){
return (list->HEAD == list) && (list->TAIL == list);
}
/* insert a node in a list after a specified node */
void lstInsertAfter(NODE* pre, NODE* _new){
NODE *next = pre->next;
pre->next = _new;
_new->next = next;
_new->previous = pre;
next->previous = _new;
}
void __lstDelete(NODE *prev, NODE *next){
next->previous = prev;
prev->next = next;
}
/* delete a specified node from a list */
void lstDelete(NODE* entry){
__lstDelete(entry->previous, entry->next);
entry->next = NULL;
entry->previous = NULL;
}
NODE *hashFind(LRUCache *obj, int key) {
if (NULL == obj) return NULL;
int slot = key % HASH_SIZE;
for (HashElement *p = obj->hashTable[slot].next; p; p = p->next) {
if (p->data->key == key)
return &p->data->node;;
}
return NULL;
}
void hashDelete(LRUCache *obj, int key) {
if (NULL == obj) return;
int slot = key % HASH_SIZE;
HashElement *pre = &obj->hashTable[slot];
HashElement *cur = obj->hashTable[slot].next;
for ( ; cur; pre = cur, cur = cur->next) {
if (cur->data->key == key) {
pre->next = cur->next;
free(cur);
return;
}
}
return;
}
void hashInsert(LRUCache *obj, DATA *node) {
if (NULL == obj || NULL == node) return;
int slot = node->key % HASH_SIZE;
HashElement *p = NULL;;
for (p = obj->hashTable[slot].next; p; p = p->next) {
if (p->data == node) return;
}
p = (HashElement *)malloc(sizeof(HashElement));
p->data = node;
p->next = obj->hashTable[slot].next;
obj->hashTable[slot].next = p;
}
LRUCache* lRUCacheCreate(int capacity) {
if (capacity < 1) return NULL;
LRUCache *obj = (LRUCache *)calloc(1, sizeof(LRUCache));
if (NULL == obj) return NULL;
obj->list = (NODE *)calloc(1, sizeof(NODE));
if (NULL == obj->list) {
free(obj);
return NULL;
}
lstInit(obj->list);
obj->capacity = capacity;
obj->size = 0;
obj->hashTable = (HashElement *)calloc(HASH_SIZE, sizeof(HashElement));
if (NULL == obj->hashTable) {
free(obj->list);
free(obj);
return NULL;
}
return obj;
}
int lRUCacheGet(LRUCache* obj, int key) {
if (NULL == obj || NULL == obj->list || NULL == obj->hashTable) return -1;
NODE *list = obj->list;
if (lstEmpty(list)) return -1;
NODE *lookup = hashFind(obj, key);
if (lookup) {
if (lookup != lstFirst(list)) {
lstDelete(lookup);
lstInsertAfter(list, lookup);
}
DATA *data = (DATA *)lookup;
return data->value;
}
return -1;
}
void lRUCachePut(LRUCache* obj, int key, int value) {
if (NULL == obj || NULL == obj->list || NULL == obj->hashTable) return;
NODE *list = obj->list;
NODE *lookup = hashFind(obj, key);
if (lookup) {
if (lookup != lstFirst(list)) {
lstDelete(lookup);
lstInsertAfter(list, lookup);
}
DATA *data = (DATA *)lookup;
data->value = value;
return;
}
DATA *New = (DATA *)calloc(1, sizeof(DATA));
if (NULL == New) return;
New->key = key;
New->value = value;
obj->size++;
if (obj->size > obj->capacity) {
obj->size--;
DATA *tail = (DATA *)lstLast(list);
hashDelete(obj, tail->key);
lstDelete(&tail->node);
free(tail);
tail = NULL;
}
lstInsertAfter(list, &New->node);
hashInsert(obj, New);
return;
}
void lRUCacheFree(LRUCache* obj) {
DATA *var = NULL;
NODE *list = obj->list;
if (obj) {
if (list) {
while (!lstEmpty(list)) {
DATA *deleted = (DATA *)lstFirst(list);
lstDelete((NODE*)deleted);
free(deleted);
}
free(list);
}
if (obj->hashTable) {
for (int i = 0; i < HASH_SIZE; i++) {
HashElement *deleted = obj->hashTable[i].next;
while (deleted) {
HashElement *next = deleted->next;
free(deleted);
deleted = next;
}
}
free(obj->hashTable);
}
free(obj);
}
}
/**
* Your LRUCache struct will be instantiated and called as such:
* LRUCache* obj = lRUCacheCreate(capacity);
* int param_1 = lRUCacheGet(obj, key);
* lRUCachePut(obj, key, value);
* lRUCacheFree(obj);
*/
static void test1();
static void test2();
static void print(LRUCache *obj);
static int c() {
test1();
test2();
return 0;
}
static void test1() {
int capacity = 2;
int value = -1;
LRUCache* obj = lRUCacheCreate(capacity);
printf("create LRU, size:%d\n", capacity);
print(obj);
lRUCachePut(obj, 1, 1);
printf("put [1, 1]\n");
print(obj);
lRUCachePut(obj, 2, 2);
printf("put [2, 2]\n");
print(obj);
value = lRUCacheGet(obj, 1);
printf("get key[1] return:%d\n", value);
print(obj);
lRUCachePut(obj, 3, 3);
printf("put [3, 3]\n");
print(obj);
value = lRUCacheGet(obj, 2);
printf("get key[2] return:%d\n", value);
print(obj);
lRUCachePut(obj, 4, 4);
printf("put [4, 4]\n");
print(obj);
value = lRUCacheGet(obj, 1);
printf("get key[1] return:%d\n", value);
print(obj);
value = lRUCacheGet(obj, 3);
printf("get key[3] return:%d\n", value);
print(obj);
value = lRUCacheGet(obj, 4);
printf("get key[4] return:%d\n", value);
print(obj);
lRUCacheFree(obj);
obj = NULL;
printf("test %s OK!\n", __FUNCTION__);
printf("========================\n\n");
}
static void test2() {
int capacity = 1;
int value = -1;
LRUCache* obj = lRUCacheCreate(capacity);
printf("create LRU, size:%d\n", capacity);
print(obj);
lRUCachePut(obj, 2, 1);
printf("put [2, 1]\n");
print(obj);
value = lRUCacheGet(obj, 1);
printf("get key[1] return:%d\n", value);
print(obj);
lRUCacheFree(obj);
obj = NULL;
printf("test %s OK!\n", __FUNCTION__);
printf("========================\n\n");
}
#define LIST_FOR_EACH(type,var,list) \
for(var = (type*)lstFirst(list); \
var != (type*)(list); \
var=(type*)(((NODE *)var)->next))
static void print(LRUCache *obj) {
if (NULL == obj || NULL == obj->list)
return;
DATA* var = NULL;
NODE *list = obj->list;
printf("%d elemens in LRU [%p]\n", obj->size, list);
LIST_FOR_EACH(DATA, var, list)
printf("key:%d\tvalue:%d\t%p\n", var->key, var->value, var);
printf("========================\n");
}
#ifdef __cplusplus
};
#endif
int main() {
cpp();
c();
return 0;
}
//Test Result:
root@ubuntu:~$ g++ 146.CRUCache.cpp -o 146.LRU -g -fstack-protector-all -fsanitize=address -fno-omit-frame-pointer -fsanitize=leak
root@ubuntu:~$ ./146.LRU
Capacity of Cache:8
set:0 [0,0]
set:1 [1,1]->[0,0]
set:2 [2,2]->[1,1]->[0,0]
set:3 [3,3]->[2,2]->[1,1]->[0,0]
set:4 [4,4]->[3,3]->[2,2]->[1,1]->[0,0]
set:5 [5,5]->[4,4]->[3,3]->[2,2]->[1,1]->[0,0]
set:6 [6,6]->[5,5]->[4,4]->[3,3]->[2,2]->[1,1]->[0,0]
set:7 [7,7]->[6,6]->[5,5]->[4,4]->[3,3]->[2,2]->[1,1]->[0,0]
set:8 [8,8]->[7,7]->[6,6]->[5,5]->[4,4]->[3,3]->[2,2]->[1,1]
set:9 [9,9]->[8,8]->[7,7]->[6,6]->[5,5]->[4,4]->[3,3]->[2,2]
set:10 [10,10]->[9,9]->[8,8]->[7,7]->[6,6]->[5,5]->[4,4]->[3,3]
set:11 [11,11]->[10,10]->[9,9]->[8,8]->[7,7]->[6,6]->[5,5]->[4,4]
set:12 [12,12]->[11,11]->[10,10]->[9,9]->[8,8]->[7,7]->[6,6]->[5,5]
set:13 [13,13]->[12,12]->[11,11]->[10,10]->[9,9]->[8,8]->[7,7]->[6,6]
set:14 [14,14]->[13,13]->[12,12]->[11,11]->[10,10]->[9,9]->[8,8]->[7,7]
set:15 [15,15]->[14,14]->[13,13]->[12,12]->[11,11]->[10,10]->[9,9]->[8,8]
Random set
set(1:6):[1,6]->[15,15]->[14,14]->[13,13]->[12,12]->[11,11]->[10,10]->[9,9]
set(6:7):[6,7]->[1,6]->[15,15]->[14,14]->[13,13]->[12,12]->[11,11]->[10,10]
set(1:3):[1,3]->[6,7]->[15,15]->[14,14]->[13,13]->[12,12]->[11,11]->[10,10]
set(3:7):[3,7]->[1,3]->[6,7]->[15,15]->[14,14]->[13,13]->[12,12]->[11,11]
set(7:7):[7,7]->[3,7]->[1,3]->[6,7]->[15,15]->[14,14]->[13,13]->[12,12]
set(4:6):[4,6]->[7,7]->[3,7]->[1,3]->[6,7]->[15,15]->[14,14]->[13,13]
set(4:6):[4,6]->[7,7]->[3,7]->[1,3]->[6,7]->[15,15]->[14,14]->[13,13]
set(6:7):[6,7]->[4,6]->[7,7]->[3,7]->[1,3]->[15,15]->[14,14]->[13,13]
set(6:6):[6,6]->[4,6]->[7,7]->[3,7]->[1,3]->[15,15]->[14,14]->[13,13]
set(0:5):[0,5]->[6,6]->[4,6]->[7,7]->[3,7]->[1,3]->[15,15]->[14,14]
set(3:1):[3,1]->[0,5]->[6,6]->[4,6]->[7,7]->[1,3]->[15,15]->[14,14]
set(6:6):[6,6]->[3,1]->[0,5]->[4,6]->[7,7]->[1,3]->[15,15]->[14,14]
set(4:1):[4,1]->[6,6]->[3,1]->[0,5]->[7,7]->[1,3]->[15,15]->[14,14]
set(3:0):[3,0]->[4,1]->[6,6]->[0,5]->[7,7]->[1,3]->[15,15]->[14,14]
set(4:5):[4,5]->[3,0]->[6,6]->[0,5]->[7,7]->[1,3]->[15,15]->[14,14]
set(6:5):[6,5]->[4,5]->[3,0]->[0,5]->[7,7]->[1,3]->[15,15]->[14,14]
set(3:4):[3,4]->[6,5]->[4,5]->[0,5]->[7,7]->[1,3]->[15,15]->[14,14]
set(4:4):[4,4]->[3,4]->[6,5]->[0,5]->[7,7]->[1,3]->[15,15]->[14,14]
set(7:7):[7,7]->[4,4]->[3,4]->[6,5]->[0,5]->[1,3]->[15,15]->[14,14]
set(3:6):[3,6]->[7,7]->[4,4]->[6,5]->[0,5]->[1,3]->[15,15]->[14,14]
set(6:7):[6,7]->[3,6]->[7,7]->[4,4]->[0,5]->[1,3]->[15,15]->[14,14]
set(4:3):[4,3]->[6,7]->[3,6]->[7,7]->[0,5]->[1,3]->[15,15]->[14,14]
set(6:3):[6,3]->[4,3]->[3,6]->[7,7]->[0,5]->[1,3]->[15,15]->[14,14]
set(2:4):[2,4]->[6,3]->[4,3]->[3,6]->[7,7]->[0,5]->[1,3]->[15,15]
Random get
get:1 [1,3]->[2,4]->[6,3]->[4,3]->[3,6]->[7,7]->[0,5]->[15,15]
get:6 [6,3]->[1,3]->[2,4]->[4,3]->[3,6]->[7,7]->[0,5]->[15,15]
get:14 cannot Get 14
get:15 [15,15]->[6,3]->[1,3]->[2,4]->[4,3]->[3,6]->[7,7]->[0,5]
get:9 cannot Get 9
get:3 [3,6]->[15,15]->[6,3]->[1,3]->[2,4]->[4,3]->[7,7]->[0,5]
get:11 cannot Get 11
get:15 [15,15]->[3,6]->[6,3]->[1,3]->[2,4]->[4,3]->[7,7]->[0,5]
get:7 [7,7]->[15,15]->[3,6]->[6,3]->[1,3]->[2,4]->[4,3]->[0,5]
get:7 [7,7]->[15,15]->[3,6]->[6,3]->[1,3]->[2,4]->[4,3]->[0,5]
get:12 cannot Get 12
get:14 cannot Get 14
get:12 cannot Get 12
get:6 [6,3]->[7,7]->[15,15]->[3,6]->[1,3]->[2,4]->[4,3]->[0,5]
get:6 [6,3]->[7,7]->[15,15]->[3,6]->[1,3]->[2,4]->[4,3]->[0,5]
get:7 [7,7]->[6,3]->[15,15]->[3,6]->[1,3]->[2,4]->[4,3]->[0,5]
get:6 [6,3]->[7,7]->[15,15]->[3,6]->[1,3]->[2,4]->[4,3]->[0,5]
get:14 cannot Get 14
get:0 [0,5]->[6,3]->[7,7]->[15,15]->[3,6]->[1,3]->[2,4]->[4,3]
get:13 cannot Get 13
get:11 cannot Get 11
get:9 cannot Get 9
get:6 [6,3]->[0,5]->[7,7]->[15,15]->[3,6]->[1,3]->[2,4]->[4,3]
get:6 [6,3]->[0,5]->[7,7]->[15,15]->[3,6]->[1,3]->[2,4]->[4,3]
========================
[4,4]->[3,3]->[2,2]->[1,1]
[5,5]->[4,4]->[3,3]->[2,2]->[1,1]
[8,8]->[7,7]->[6,6]->[5,5]->[4,4]
[0,0]->[9,9]->[8,8]->[7,7]->[6,6]
========================
create LRU, size:2
0 elemens in LRU [0x602000000010]
========================
put [1, 1]
1 elemens in LRU [0x602000000010]
key:1 value:1 0x603000000d00
========================
put [2, 2]
2 elemens in LRU [0x602000000010]
key:2 value:2 0x603000000d30
key:1 value:1 0x603000000d00
========================
get key[1] return:1
2 elemens in LRU [0x602000000010]
key:1 value:1 0x603000000d00
key:2 value:2 0x603000000d30
========================
put [3, 3]
2 elemens in LRU [0x602000000010]
key:3 value:3 0x603000000d60
key:1 value:1 0x603000000d00
========================
get key[2] return:-1
2 elemens in LRU [0x602000000010]
key:3 value:3 0x603000000d60
key:1 value:1 0x603000000d00
========================
put [4, 4]
2 elemens in LRU [0x602000000010]
key:4 value:4 0x603000000d90
key:3 value:3 0x603000000d60
========================
get key[1] return:-1
2 elemens in LRU [0x602000000010]
key:4 value:4 0x603000000d90
key:3 value:3 0x603000000d60
========================
get key[3] return:3
2 elemens in LRU [0x602000000010]
key:3 value:3 0x603000000d60
key:4 value:4 0x603000000d90
========================
get key[4] return:4
2 elemens in LRU [0x602000000010]
key:4 value:4 0x603000000d90
key:3 value:3 0x603000000d60
========================
test test1 OK!
========================
create LRU, size:1
0 elemens in LRU [0x6020000000b0]
========================
put [2, 1]
1 elemens in LRU [0x6020000000b0]
key:2 value:1 0x603000000df0
========================
get key[1] return:-1
1 elemens in LRU [0x6020000000b0]
key:2 value:1 0x603000000df0
========================
test test2 OK!
========================