LeetCode //C - 146. LRU Cache

146. 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.
 

Example 1:

Input:
[“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
Output:
[null, null, null, 1, null, -1, null, -1, 3, 4]
Explanation:
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // cache is {1=1}
lRUCache.put(2, 2); // cache is {1=1, 2=2}
lRUCache.get(1); // return 1
lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3}
lRUCache.get(2); // returns -1 (not found)
lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3}
lRUCache.get(1); // return -1 (not found)
lRUCache.get(3); // return 3
lRUCache.get(4); // return 4

Constraints:

  • 1 <= capacity <= 3000
  • 0 < = k e y < = 1 0 4 0 <= key <= 10^4 0<=key<=104
  • 0 < = v a l u e < = 1 0 5 0 <= value <= 10^5 0<=value<=105
  • At most 2 ∗ 1 0 5 2 * 10^5 2105 calls will be made to get and put.

From: LeetCode
Link: 146. LRU Cache


Solution:

Ideas:

The idea behind the code is to implement an LRU (Least Recently Used) Cache, which has a fixed capacity and supports two primary operations: get and put.

  1. LRUCache(int capacity): Initialize the LRU cache with a given capacity.
  2. int get(int key): Return the value of the key if the key exists, otherwise return -1.
  3. 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 cache exceeds its capacity, remove the least recently used item.
    Here’s how these functionalities are achieved:

Data Structures Used:

  1. Doubly-Linked List: Each node contains a key-value pair. The order of the nodes in the list represents the usage order. The head of the list contains the most recently used item, and the tail contains the least recently used item.

  2. Hash Table: This is used for quick look-up to find whether a key exists in the cache or not. The hash table stores pointers to the nodes in the doubly-linked list.

Operations:
Initialization (lRUCacheCreate):

  • Initialize an empty hash table and an empty doubly-linked list with a given capacity.

Get (lRUCacheGet):

  1. Look up the key in the hash table.
  2. If the key exists, move the corresponding node to the head of the doubly-linked list to mark it as recently used.
  3. Return the value of the node.

Put (lRUCachePut):

  1. Check if the key already exists.
  • If it does, update its value and move it to the head of the list.
  • If it doesn’t, create a new node and add it to the head of the list.
  1. If adding a new node exceeds the cache’s capacity:
  • Remove the tail node (least recently used item) from the doubly-linked list.
  • Also remove it from the hash table.

Free (lRUCacheFree):

  • Free the allocated memory for both the doubly-linked list and the hash table.
Code:
typedef struct Node {
    
    
    int key;
    int value;
    struct Node* prev;
    struct Node* next;
} Node;

// Define the LRU Cache struct
typedef struct {
    
    
    int capacity;
    int size;
    Node* head;
    Node* tail;
    Node** hash_table;
} LRUCache;

// Helper function to create a new Node
Node* createNode(int key, int value) {
    
    
    Node* new_node = (Node*) malloc(sizeof(Node));
    new_node->key = key;
    new_node->value = value;
    new_node->prev = NULL;
    new_node->next = NULL;
    return new_node;
}

LRUCache* lRUCacheCreate(int capacity) {
    
    
    LRUCache* obj = (LRUCache*) malloc(sizeof(LRUCache));
    obj->capacity = capacity;
    obj->size = 0;
    obj->head = NULL;
    obj->tail = NULL;
    obj->hash_table = (Node**) calloc(10005, sizeof(Node*)); // Assuming maximum key is 10^4
    return obj;
}

int lRUCacheGet(LRUCache* obj, int key) {
    
    
    Node* node = obj->hash_table[key];
    if (node == NULL) {
    
    
        return -1;
    }
    
    // Move the accessed node to the head
    if (node != obj->head) {
    
    
        node->prev->next = node->next;
        if (node->next != NULL) {
    
    
            node->next->prev = node->prev;
        } else {
    
    
            obj->tail = node->prev;
        }
        node->prev = NULL;
        node->next = obj->head;
        obj->head->prev = node;
        obj->head = node;
    }
    return node->value;
}

void lRUCachePut(LRUCache* obj, int key, int value) {
    
    
    Node* node = obj->hash_table[key];
    if (node != NULL) {
    
    
        // Update the value and move the node to the head
        node->value = value;
        if (node != obj->head) {
    
    
            node->prev->next = node->next;
            if (node->next != NULL) {
    
    
                node->next->prev = node->prev;
            } else {
    
    
                obj->tail = node->prev;
            }
            node->prev = NULL;
            node->next = obj->head;
            obj->head->prev = node;
            obj->head = node;
        }
    } else {
    
    
        // Add a new node
        Node* new_node = createNode(key, value);
        obj->hash_table[key] = new_node;
        
        if (obj->size == 0) {
    
    
            obj->head = new_node;
            obj->tail = new_node;
        } else {
    
    
            new_node->next = obj->head;
            obj->head->prev = new_node;
            obj->head = new_node;
        }
        obj->size++;
        
        // Remove the LRU element if the cache is full
        if (obj->size > obj->capacity) {
    
    
            obj->hash_table[obj->tail->key] = NULL;
            Node* prev_tail = obj->tail;
            obj->tail = obj->tail->prev;
            obj->tail->next = NULL;
            free(prev_tail);
            obj->size--;
        }
    }
}

void lRUCacheFree(LRUCache* obj) {
    
    
    Node* temp = obj->head;
    while (temp != NULL) {
    
    
        Node* next = temp->next;
        free(temp);
        temp = next;
    }
    free(obj->hash_table);
    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);
*/

猜你喜欢

转载自blog.csdn.net/navicheung/article/details/132643768