LeetCode 0146. Caché LRU: lista doblemente enlazada + hash

【LetMeFly】Caché 146.LRU: lista doblemente enlazada + hash

Enlace de pregunta de Likou: https://leetcode.cn/problems/lru-cache/

Diseñe e implemente una estructura de datos que satisfaga  las restricciones de caché LRU (menos recientemente utilizada) .
Clase de implementación LRUCache :
  • LRUCache(int capacity)Inicialice la caché LRU con un número entero positivo como capacidad capacity
  • int get(int key)Si la palabra clave keyexiste en la memoria caché, se devuelve el valor de la palabra clave; de ​​lo contrario, se devuelve -1.
  • void put(int key, int value) Si la palabra clave  keyya existe, cambie su valor de datos  value; si no existe, inserte el grupo en la caché  key-value. Si una operación de inserción hace que se exceda el número de palabras clave  , capacityse debe desalojar la palabra clave más antigua no utilizada .

Funciona gety putdebe O(1)ejecutarse con una complejidad de tiempo promedio de .

 

Ejemplo:

Entrada 
["LRUCache", "poner", "poner", "obtener", "poner", "obtener", "poner", "obtener", "obtener", "obtener"] [[2], [1 
, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] salida [nulo, nulo, nulo , 
1 , null, -1, null, -1, 3, 4] Explicación de 
LRUCache lRUCache = new LRUCache(2); 
lRUCache.put(1, 1); // El caché es {1=1} 
lRUCache.put(2 , 2 ); // El caché es {1=1, 2=2} 
lRUCache.get(1); // Devuelve 1 
lRUCache.put(3, 3); // Esta operación invalidará la palabra clave 2 y el caché es {1 =1, 3=3} 
lRUCache.get(2); // Devuelve -1 (no encontrado) 
lRUCache.put(4, 4); // Esta operación invalidará la palabra clave 1 y el caché es {4= 4, 3=3} 
lRUCache.get(1); // Devuelve -1 (no encontrado) 
lRUCache.get(3); // Devuelve 3 
lRUCache.get(4); // Devuelve 4



 

pista:

  • 1 <= capacity <= 3000
  • 0 <= key <= 10000
  • 0 <= value <= 105
  • En la mayoría de los momentos de llamada y2 * 105getput

Método 1: lista doblemente enlazada + hash

Utilice uno 双向链表como caché LRU. Los nodos más cercanos al encabezado de la lista enlazada se utilizan más recientemente.

Utilice una tabla hash para implementar la función de keymapeo de a 节点.

Para asignar desde el nodo al valor clave de la tabla hash , keytambién se almacena en el nodo una copia adicional del keyvalor del nodo:

class LRU_Node {
    
    
public:
    LRU_Node* previous, *next;
    int key, value;
}

Para facilitar la operación, puede agregar un nodo vacío al principio y al final de la lista doblemente vinculada para evitar el juicio especial de "si está vacío".

Para obtener operación:

Si existe en la tabla hash key, la tabla hash asigna el nodo, el nodo se mueve al primer nodo de la lista vinculada y se devuelve el nodo value.

Si no existe en la tabla hash key, se devolverá directamente -1.

Para operación de venta:

Si el nodo existe en la tabla hash key, la tabla hash asigna el nodo, el valor del nodo se actualiza y el nodo se mueve al primer nodo de la lista vinculada.

Si el nodo no existe en la tabla hash key, cree el nodo y colóquelo como el primer nodo en la lista vinculada. Si la capacidad de la tabla hash es mayor que la capacidad máxima, tail.previousse obtiene el último nodo, el nodo se elimina de la tabla hash keyy el nodo se elimina de la lista vinculada.

  • Complejidad temporal: la complejidad temporal de cada operación es O (1) O (1)o ( 1 )
  • Complejidad del espacio O (max (put, capacidad)) O(max(put, capacidad))O ( máx ( poner , _ _ _capacidad ) ) _ _ _ _ _ _

código de CA

C++
class LRU_Node {
    
    
public:
    LRU_Node* previous, *next;
    int key, value;
    LRU_Node(LRU_Node* previous, LRU_Node* next, int key, int value) {
    
    
        this->previous = previous;
        this->next = next;
        this->key = key;
        this->value = value;
    }
};

class LRUCache {
    
    
private:
    LRU_Node* head, *tail;
    int capacity;
    unordered_map<int, LRU_Node*> ma;

    void refresh(int key, int value) {
    
    
        LRU_Node* thisNode = ma[key];
        thisNode->value = value;

        LRU_Node* previous = thisNode->previous, *next = thisNode->next;
        previous->next = next, next->previous = previous;
        
        thisNode->next = head->next;
        head->next = thisNode;
        thisNode->previous = head;
        thisNode->next->previous = thisNode;
    }
public:
    LRUCache(int capacity) {
    
    
        head = new LRU_Node(nullptr, nullptr, 0, 0);
        tail = new LRU_Node(head, nullptr, 0, 0);
        head->next = tail;
        this->capacity = capacity;
    }
    
    int get(int key) {
    
    
        if (ma.count(key)) {
    
    
            refresh(key, ma[key]->value);
            return ma[key]->value;
        }
        return -1;
    }
    
    void put(int key, int value) {
    
    
        if (ma.count(key)) {
    
    
            refresh(key, value);
            return;
        }
        LRU_Node* thisNode = new LRU_Node(head, head->next, key, value);
        ma[key] = thisNode;
        head->next = thisNode, thisNode->next->previous = thisNode;
        if (ma.size() > capacity) {
    
    
            LRU_Node* toRemove = tail->previous;
            ma.erase(toRemove->key);
            toRemove->previous->next = tail;
            tail->previous = toRemove->previous;
        }
    }

    void debug() {
    
    
        cout << "Now size: " << ma.size() << ": [";
        LRU_Node* p = head->next;
        while (p != tail) {
    
    
            if (p != head->next) {
    
    
                cout << ", ";
            }
            cout << "(" << p->key << "|" << p->value << ")";
            p = p->next;
        }
        cout << "] | [";
        p = tail->previous;
        while (p != head) {
    
    
            if (p != tail->previous) {
    
    
                cout << ", ";
            }
            cout << "(" << p->key << "|" << p->value << ")";
            p = p->previous;
        }
        cout << "]" << endl;
    }
};
Pitón
class LRU_Node:
    
    def __init__(self, previous, next, key, value):
        self.previous = previous
        self.next = next
        self.key = key
        self.value = value


class LRUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.head = LRU_Node(None, None, 0, 0)
        self.tail = LRU_Node(self.head, None, 0, 0)
        self.head.next = self.tail
        self.ma = dict()
    

    def move2first(self, thisNode: LRU_Node):
        thisNode.previous.next = thisNode.next
        thisNode.next.previous = thisNode.previous
        
        thisNode.previous = self.head
        thisNode.next = self.head.next
        self.head.next = thisNode
        thisNode.next.previous = thisNode


    def get(self, key: int) -> int:
        if key in self.ma:
            self.move2first(self.ma[key])
            return self.ma[key].value
        return -1


    def put(self, key: int, value: int) -> None:
        if key in self.ma:
            thisNode = self.ma[key]
            thisNode.value = value
            self.move2first(thisNode)
        else:
            thisNode = LRU_Node(self.head, self.head.next, key, value)
            self.ma[key] = thisNode
            self.head.next = thisNode
            thisNode.next.previous = thisNode
            if len(self.ma) > self.capacity:
                toRemove = self.tail.previous
                del self.ma[toRemove.key]
                toRemove.previous.next = self.tail
                self.tail.previous = toRemove.previous

El artículo se publica simultáneamente en CSDN. No es fácil ser original. Adjunte el enlace al artículo original después de la reimpresión con el consentimiento del autor ~
Tisfy: https://letmefly.blog.csdn.net/article/details/133241877

Supongo que te gusta

Origin blog.csdn.net/Tisfy/article/details/133241877
Recomendado
Clasificación