146. LRU Cache [LeeCode]

本文介绍了一种遵循最近最少使用(LRU)原则的缓存数据结构的设计与实现方法,提供了C++和C两种语言的具体实现方案,并通过实例展示了如何使用该缓存结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

146LRU 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!
========================

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luuyiran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值