leetcode-pp / 91alg-10-daily-check

第X期打卡仓库
8 stars 0 forks source link

【Day 12 】2023-02-25 - 146. LRU 缓存机制 #15

Open azl397985856 opened 1 year ago

azl397985856 commented 1 year ago

146. LRU 缓存机制

入选理由

暂无

题目地址

https://leetcode-cn.com/problems/lru-cache/

前置知识

暂无

题目描述

运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:

LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:

输入

["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释

LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4
Yufanzh commented 1 year ago
# hashmap + doubly linked list
# hashmap key: key, value: doubly linked node
# create a doubly linked node class for convenience
class Node:
    def __init__(self, key, val):
        """ The node class store(key, val)
            this Node is a doubly linked node,
            so it should also initialize prev and next pointer
        """
        self.key = key
        self.val = val
        self.prev = None
        self.next = None

class LRUCache:

    def __init__(self, capacity: int):
        #initialize a hashmap and a doubly linked list
        self.cap = capacity
        self.cache = {}

        # initialize two nodes to define boundary
        self.left = Node(0, 0) # lru  
        self.right = Node(0, 0) # mru
        # generate doubly linked list
        self.left.next = self.right
        self.right.prev = self.left

    def get(self, key: int) -> int:
        """" if key exists, return its value and insert it to mru node
            if key not exists, return -1
        """
        if key not in self.cache:
            return -1
        else:
            self.remove(self.cache[key])
            self.insert(self.cache[key])
            return self.cache[key].val

    def put(self, key: int, value: int) -> None:
        """ if key exists, we need to remove and re-insert to mru
            if key not exists, just insert to mru
            we also need to deal with exceeding capacity situation
        """
        if key in self.cache:
            self.remove(self.cache[key])
        # put to hashmap
        self.cache[key] = Node(key, value)
        self.insert(self.cache[key])

        if len(self.cache) > self.cap:
            # remove lru first
            lru = self.left.next
            self.remove(lru)
            del self.cache[lru.key]

    def insert(self, node: Node) -> None:
        """ insert node before mru
            example a <-> node <--> right
        """
        prv = self.right.prev
        prv.next = node
        node.prev = prv
        node.next = self.right
        self.right.prev = node

    def remove(self, node: Node) -> None: 
        """ remove node from a doubly linked list
            example a <-> b <-> c
        """
        prv = node.prev 
        nxt = node.next
        prv.next = nxt
        nxt.prev = prv 

# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
JiangyanLiNEU commented 1 year ago

OrderedDict

class LRUCache:

def __init__(self, capacity: int):
    self.cache = {}
    self.capacity = capacity
    self.left = Node(0,0)
    self.right = Node(0,0)
    self.left.next = self.right
    self.right.prev = self.left

def get(self, key: int) -> int:
    if key in self.cache:
        node = self.cache[key]
        self.remove(node)
        self.insert(node)
        return node.value
    return -1

def put(self, key: int, value: int) -> None:
    if key in self.cache:
        node = Node(key, value)
        self.remove(self.cache[key])
        self.insert(node)
        self.cache[key] = node
    else:
        if len(self.cache) == self.capacity:
            self.remove(self.right.prev)
        node = Node(key, value)
        self.insert(node)
def remove(self, node):
    prev = node.prev
    after = node.next
    prev.next = after
    after.prev = prev
    del self.cache[node.key]
def insert(self, node):
    one = self.left.next
    self.left.next = node
    node.prev = self.left
    node.next = one
    one.prev = node
    self.cache[node.key] = node
X1AOX1A commented 1 year ago

题目

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
**函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。**

示例:

输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

关键点:函数 get 和 put 必须以 O(1) 的平均时间复杂度运行

思路:双向链表+哈希表

代码

class Node:
    def __init__(self, key, val):
        self.key = key
        self.val = val
        self.prev = None
        self.next = None

class LRUCache:

    def __init__(self, capacity: int):        
        self.nums = 0
        self.capacity = capacity        
        # lookup table, {key: key, value: node}
        self.lookup = dict()  
        # doubly linkedlist, [head<->old_node<->new_node<->tail]
        self.head = Node(None, None)
        self.tail = Node(None, None)
        self.head.next = self.tail
        self.tail.prev = self.head

    def get(self, key: int) -> int:
        if key not in self.lookup:
            return -1
        # 1. get the node
        node = self.lookup[key]
        # 2. remove node
        self.remove(node)
        # 3. add node to the tail
        self.add_to_tail(node)
        return node.val

    def put(self, key: int, value: int) -> None:
        # 1. remove the old node, if exist
        if key in self.lookup:
            self.remove(self.lookup[key])
            self.nums -= 1
        # 2. remove oldest node if full
        if self.nums == self.capacity:
            self.remove(self.head.next)
            self.nums -= 1
        # 3. add the node to the tail
        self.add_to_tail(Node(key, value))
        self.nums += 1

    def remove(self, node):
        # remove node from lookup and doubly linkedlist
        del self.lookup[node.key]
        node.prev.next = node.next
        node.next.prev = node.prev

    def add_to_tail(self, node):
        # add node to lookup and the tail (before tail)
        self.lookup[node.key] = node
        pre_tail = self.tail.prev
        node.next = self.tail
        self.tail.prev = node
        pre_tail.next = node
        node.prev = pre_tail
RestlessBreeze commented 1 year ago

code

class LRUCache {
public:
    struct LRUNode
    {
        int key;
        int val;
        LRUNode* prev;
        LRUNode* next;
        LRUNode() : key(0), val(0), prev(nullptr), next(nullptr) {}
        LRUNode(int _key, int _val) : key(_key), val(_val), prev(nullptr), next(nullptr) {}
    };

    LRUNode* front;
    LRUNode* end;
    int cap;
    int nc;
    unordered_map<int, LRUNode*> hash;

    LRUCache(int capacity) {
        front = new LRUNode();
        end = new LRUNode();
        front -> next = end;
        end -> prev = front;
        cap = capacity;
        nc = 0;
    }

    int get(int key) {
        int res = -1;
        if (hash.count(key))
        {
            res = hash[key] -> val;
            deleteFromlist(hash[key]);
            addTofront(hash[key]);
        }
        return res;
    }

    void put(int key, int value) {
        if (hash.count(key))
        {
            hash[key] -> val = value;
            deleteFromlist(hash[key]);
            addTofront(hash[key]);    
        }
        else
        {
            if (nc == cap)
            {
                nc--;
                hash.erase(end -> prev -> key);
                deleteFromend();
            }
            nc++;
            LRUNode* temp = new LRUNode(key, value);
            addTofront(temp);  
            hash[key] = temp; 
        }
    }

    void addTofront(LRUNode* temp)
    {
        temp -> next = front -> next;
        front -> next -> prev = temp;
        front -> next = temp;
        temp -> prev = front;
    }

    void deleteFromend()
    {
        LRUNode* temp = end -> prev;
        temp -> prev -> next = end;
        end -> prev = temp -> prev;
    }

    void deleteFromlist(LRUNode* p)
    {
        LRUNode* q = p -> prev;
        q -> next = p -> next;
        p -> next -> prev = q;
    }
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */
yingchehu commented 1 year ago

思路

使用 hash map 讓 get 可以 O(1) 使用雙向 linked list 讓刷新 key 最後使用順序,以及找出最不常用到的 key,都可以在 O(1) 完成

Code

class LRUCache:

    def __init__(self, capacity: int):
        self.size = capacity
        self.map = {}
        self.head = ListNode(-1, -1)
        self.tail = ListNode(-1, -1)
        self.head.right = self.tail
        self.tail.left = self.head

    def get(self, key: int) -> int:
        if key in self.map:
            node = self.map[key]
            # move to right side of the head 
            node.left.right = node.right
            node.right.left = node.left
            node.right = self.head.right
            node.left = self.head
            self.head.right.left = node
            self.head.right = node
            return node.val
        else:
            return -1

    def put(self, key: int, value: int) -> None:
        if key in self.map:
            node = self.map[key]
            # move to right side of the head 
            node.left.right = node.right
            node.right.left = node.left
            node.right = self.head.right
            node.left = self.head
            self.head.right.left = node
            self.head.right = node
            # update value
            node.val = value
        else:
            if len(self.map) == self.size:
                # evict the least recently used key
                node = self.tail.left
                del self.map[node.key]
                self.tail.left = node.left
                node.left.right = self.tail
            # put new node
            node = ListNode(key, value)
            self.map[key] = node
            node.left = self.head
            node.right = self.head.right
            self.head.right.left = node
            self.head.right = node
        return

class ListNode:
    def __init__(self, key, val):
        self.key = key
        self.val = val
        self.left = None
        self.right = None

複雜度

Time: O(1) Space: O(N), N = capacity

kofzhang commented 1 year ago

思路

OrderedDict

复杂度

时间复杂度:O(1) 空间复杂度:O(N)

代码

class LRUCache:

    def __init__(self, capacity: int):
        self.cache = OrderedDict()
        self.size = capacity

    def get(self, key: int) -> int:
        if key  not in self.cache:
            return -1
        value = self.cache.pop(key)
        self.cache[key]=value
        return value

    def put(self, key: int, value: int) -> None:
        if key not in self.cache and len(self.cache)>=self.size:
            self.cache.popitem(last=False)
        if key in self.cache:
            self.cache.pop(key)
        self.cache[key]=value
kiirii4 commented 1 year ago

思路

为使两个函数达到O(1)复杂度,同时使用链表和哈希表

代码

struct ListNodes {
    ListNodes* prev;
    ListNodes* next;
    int key, val;
    ListNodes(): key(0), val(0), prev(nullptr), next(nullptr){}
    ListNodes(int _key, int _value): key(_key), val(_value), prev(nullptr), next(nullptr){}
};

class LRUCache {
private:
    unordered_map<int, ListNodes*> hashmap;
    ListNodes* head;
    ListNodes* tail;
    int capacity, size;

public:
    LRUCache(int _capacity): capacity(_capacity), size(0) {
        head = new ListNodes();
        tail = new ListNodes();
        head->next = tail;
        tail->prev = head;
    }

    int get(int key) {
        if(!hashmap.count(key))
            return -1;
        ListNodes* node = hashmap[key];
        moveTohead(node);
        return node->val;
    }

    void put(int key, int value) {
        if(!hashmap.count(key)){
            ListNodes* node = new ListNodes(key, value);
            hashmap[key] = node;
            addTohead(node);
            ++size;
            if(size > capacity){
                ListNodes* removed = removeTail();
                hashmap.erase(removed->key);
                --size;
                delete removed;
            }
        }
        else {
            ListNodes* node = hashmap[key];
            node->val = value;
            moveTohead(node);
        }
    }

    void addTohead(ListNodes* node){
        node->prev = head;
        node->next = head->next;
        head->next->prev = node;
        head->next = node;
    }

    void removeNode(ListNodes* node){
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }

    void moveTohead(ListNodes* node){
        removeNode(node);
        addTohead(node);
    }

    ListNodes* removeTail(){
        ListNodes* node = tail->prev;
        removeNode(node);
        return node;
    }
};

复杂度

时间:O(1) 空间:O(N)

NorthSeacoder commented 1 year ago

Map

/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
    this.size = capacity;
    //Map对象能够保留键的插入顺序
    this.map = new Map();
};

/**
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {
    let val = -1;
    if (this.map.has(key)) {
        val = this.map.get(key);
        this.map.delete(key);
        //重新插入 map,调整顺序
        this.map.set(key, val);
    }
    return val;
};

/**
 * @param {number} key
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
    if (this.map.has(key)) {
        this.map.delete(key);
    }
    this.map.set(key, value);
    if (this.map.size > this.size) {
        //keys()返回一个迭代器对象,第一次调用 next 返回第一次被插入的数据
        //此时为最近未使用过的数据
        this.map.delete(this.map.keys().next().value);
    }
};

双向链表

class ListNode {
    constructor(key, value) {
        this.value = value;
        this.key = key;
        this.pre = null;
        this.next = null;
    }
}
/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
    this.max = capacity;
    //记录key-node
    this.map = new Map();
    //虚拟头尾
    this.head = new ListNode();
    this.tail = new ListNode();
    this.head.next = this.tail;
    this.tail.pre = this.head;
    this.moveToHead = (node) => {
        this.removeNode(node);
        this.appendToHead(node);
    };
    this.removeNode = (node) => {
        const pre = node.pre;
        const next = node.next;
        pre.next = next;
        next.pre = pre;
    };
    this.appendToHead = (node) => {
        node.pre = this.head;
        node.next = this.head.next;
        this.head.next.pre = node;
        this.head.next = node;
    };
    this.limit = () => {
        if (this.map.size > this.max) {
            const node = this.tail.pre;
            this.removeNode(node);
            this.map.delete(node.key);
        }
    };
};

/**
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {
    if (this.map.has(key)) {
        const node = this.map.get(key);
        this.moveToHead(node);
        return node.value;
    }
    return -1;
};

/**
 * @param {number} key
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
    if (this.map.has(key)) {
        const node = this.map.get(key);
        node.value = value;
        this.moveToHead(node);
    } else {
        const node = new ListNode(key, value);
        this.appendToHead(node);
        this.map.set(key, node);
        this.limit();
    }
};
Meisgithub commented 1 year ago
struct Node
{
    int key;
    int val;
    Node* pre;
    Node* next;
    Node() : key(-1), val(0), pre(nullptr), next(nullptr) {}
    Node(int _key, int _val) : key(_key), val(_val), pre(nullptr), next(nullptr) {}
};

class LRUCache {
private:
    int size;
    int m_capacity;
    unordered_map<int, Node*> hashtable;
    Node* head = new Node();
    Node* tail = new Node();

private:
    void moveHead(Node* node)
    {
        if (head->next == node)
        {
            return;
        }
        // cout << node->key << endl;
        Node* nodePre = node->pre;
        Node* headNext = head->next;
        nodePre->next = node->next;
        node->next->pre = nodePre;
        head->next = node;
        node->pre = head;
        node->next = headNext;
        headNext->pre = node;
    }

    void insertHead(Node* node)
    {
        Node* headNext = head->next;
        head->next = node;
        node->pre = head;
        node->next = headNext;
        headNext->pre = node;
    }

    void removeLast()
    {
        Node* del = tail->pre;
        // cout << del->key << endl;
        Node* delPre = del->pre;
        delPre->next = tail;
        tail->pre = delPre;
    }

    void print()
    {
        Node* p = head;
        while (p)
        {
            cout << p->key << ' ';
            p = p->next;
        }
        cout << endl;
    }

public:
    LRUCache(int capacity) {
        size = 0;
        m_capacity = capacity;
        hashtable.clear();
        // head
        head->next = tail;
        tail->pre = head;
    }

    int get(int key) {
        // cout << key << endl;
        if (hashtable.count(key))
        {
            Node* node = hashtable[key];
            // cout << node->val << endl;
            moveHead(node);
            // cout << "success" << endl;
            // print();
            return node->val;
        }
        // print();
        return -1;
    }

    void put(int key, int value) {
        // cout << key << endl;
        if (hashtable.count(key))
        {
            Node* node = hashtable[key];
            node->val = value;
            moveHead(node);
        }
        else
        {
            Node* newNode = new Node(key, value);
            hashtable[key] = newNode;
            if (size == m_capacity)
            {
                int delKey = tail->pre->key;
                // cout << delKey << endl;
                hashtable.erase(delKey);
                removeLast();
            }
            else
            {
                ++size;
            }
            insertHead(newNode);
        }
        // print();
    }
};
FireHaoSky commented 1 year ago

思路:

""" 通过一个字典和链表来存储键值和顺序,通过设链表的头尾,用于链表指针的确定 """

解题:python

class MyNode: def init(self, key=0, value=0): self.key = key self.value = value self.next = None self.prev = None

class LRUCache:

def __init__(self, capacity: int):
    self.cashe = dict()
    self.head = MyNode()
    self.tail = MyNode()
    self.head.next = self.tail
    self.tail.prev = self.head
    self.size = 0
    self.capacity = capacity

def get(self, key: int) -> int:
    if key not in self.cashe:
        return -1

    node = self.cashe[key]
    self.moveToHead(node)
    return node.value

def put(self, key: int, value: int) -> None:
    if key in self.cashe:
        node = self.cashe[key]
        node.value = value
        self.moveToHead(node)
    else:
        node = MyNode(key, value)
        self.cashe[key] = node
        self.addToHead(node)
        self.size+=1

        if self.size>self.capacity:
            removed = self.removeTail()
            self.cashe.pop(removed.key)
            self.size-=1

def removeNode(self,node):
    node.prev.next = node.next
    node.next.prev = node.prev

def addToHead(self,node):
    node.prev = self.head
    node.next = self.head.next
    self.head.next.prev = node
    self.head.next = node

def moveToHead(self,node):
    self.removeNode(node)
    self.addToHead(node)

def removeTail(self):
    node = self.tail.prev
    self.removeNode(node)
    return node

复杂度分析:

""" 时间复杂度:O(1) 空间复杂度:O(capacity) """

bookyue commented 1 year ago

TC: get: O(1) put: O(1)
SC: O(n)

class LRUCache {
    private final DLinkedList list;
    private final Map<Integer, DLinkedList.DLinkedNode> map;
    private final int capacity;

    public LRUCache(int capacity) {
        list = new DLinkedList();
        map = new HashMap<>();
        this.capacity = capacity;
    }

    public int get(int key) {
        if (!map.containsKey(key)) return -1;

        var node = map.get(key);
        list.moveToHead(node);
        return node.getVal();
    }

    public void put(int key, int value) {
        if (map.containsKey(key)) {
            var node = map.get(key);
            node.setVal(value);
            list.moveToHead(node);
            return;
        }

        if (capacity == map.size())
            map.remove(list.deleteTail().getKey());

        var node = new DLinkedList.DLinkedNode(key, value);
        map.put(key, node);
        list.addHead(node);
    }
}

class DLinkedList {
    private final DLinkedNode dummyHead;
    private final DLinkedNode dummyTail;

    public DLinkedList() {
        dummyHead = new DLinkedNode();
        dummyTail = new DLinkedNode();
        dummyHead.next = dummyTail;
        dummyTail.prev = dummyHead;
    }

    public void moveToHead(DLinkedNode node) {
        removeNode(node);
        addHead(node);
    }

    public DLinkedNode deleteTail() {
        var tail = dummyTail.prev;
        removeNode(tail);
        return tail;
    }

    public void addHead(DLinkedNode node) {
        node.prev = dummyHead;
        node.next = dummyHead.next;
        dummyHead.next.prev = node;
        dummyHead.next = node;
    }

    private void removeNode(DLinkedNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    public static class DLinkedNode {
        private int key;
        private int val;
        private DLinkedNode prev;
        private DLinkedNode next;

        DLinkedNode() {
        }

        DLinkedNode(int key, int val) {
            this.key = key;
            this.val = val;
        }

        public int getKey() {
            return key;
        }

        public int getVal() {
            return val;
        }

        public void setVal(int val) {
            this.val = val;
        }
    }
}
Zoeyzyzyzy commented 1 year ago
//Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.
//Use doubly LinkedList to implement get & put with O(1) time complexity
//Compared to singly linkedlist, the delete operation is pretty efficient, because we know current node's previous one
//In this LRU, we need HashMap to help us restore key & node, to help us easily find target.
//we need doubly linkedlist to help us record most & least recently used.
//use 4 helper functions: addToHead, deleteTail, removeNode, moveNodeToHead.
//remember when get/put, we need update not only doubly linkedlist but also hashMap.

// Time Complexity: O(1), Space Complexity:O(capacity)
class LRUCache {
    class DNode {
        DNode pre;
        DNode next;
        int val;
        int key;

        DNode() {
        }

        DNode(int key, int value) {
            this.key = key;
            this.val = value;
        }
    }

    private HashMap<Integer, DNode> map = new HashMap<>();
    private DNode dummHead;
    private DNode dummTail;
    private int size, capacity;

    public LRUCache(int capacity) {
        this.dummHead = new DNode();
        this.dummTail = new DNode();
        dummHead.next = dummTail;
        dummTail.pre = dummHead;
        this.size = 0;
        this.capacity = capacity;
    }

    public int get(int key) {
        DNode cur = map.get(key);
        if (cur == null)
            return -1;
        moveNodeToHead(cur);
        return cur.val;

    }

    public void put(int key, int value) {
        if (map.containsKey(key)) {
            DNode cur = map.get(key);
            cur.val = value;
            moveNodeToHead(cur);
        } else {
            DNode newNode = new DNode(key, value);
            map.put(key, newNode);
            addToHead(newNode);
            size++;
            while (size > capacity) {
                DNode res = deleteTail();
                map.remove(res.key);
                size--;
            }
        }
    }

    private void addToHead(DNode node) {
        // DNode temp = dummHead.next;
        node.pre = dummHead;
        node.next = dummHead.next;
        dummHead.next.pre = node;
        dummHead.next = node;
    }

    private void removeNode(DNode node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }

    private void moveNodeToHead(DNode node) {
        removeNode(node);
        addToHead(node);
    }

    private DNode deleteTail() {
        DNode res = dummTail.pre;
        removeNode(res);
        return res;
    }
Leonalhq commented 1 year ago

思路大混乱,照着lc和答案抄了一下+

 代码

class Node:  
    def __init__(self, key, val):  
        self.key = key  
        self.val = val  
        self.prev = None  
        self.next = None  

class LRUCache:  

    def __init__(self, capacity: int):  
        # 构建双向链表首尾节点, 使之相连  
        self.head = Node(0, 0)  
        self.tail = Node(0, 0)  
        self.head.next = self.tail  
        self.tail.prev = self.head  
        # 记录位置
        self.lookup = dict()  
        self.max_len = capacity  

    def get(self, key: int) -> int:  
        if key in self.lookup:  
            node = self.lookup[key]  
            self.remove(node)  
            self.add(node)  
            return node.val  
        else:  
            return -1  

    def put(self, key: int, value: int) -> None:  
        if key in self.lookup:  
            self.remove(self.lookup[key])  
        #满了要删掉了
        if len(self.lookup) == self.max_len:  
            self.remove(self.head.next)  
        self.add(Node(key, value))
    # 删除链表节点  
    def remove(self, node):  
        del self.lookup[node.key]  
        node.prev.next = node.next  
        node.next.prev = node.prev  
    # 加在链表尾  
    def add(self, node): 
        # 记录每个 key 对应的链表节点引用 
        self.lookup[node.key] = node  
        #+到尾巴之前
        pre_tail = self.tail.prev  
        node.next = self.tail  
        self.tail.prev = node  
        pre_tail.next = node  
        node.prev = pre_tail


**复杂度分析**  
- 时间复杂度:O(1),其中 N 为数组长度。  
- 空间复杂度:O(N)
AstrKing commented 1 year ago

思路

1、先了解lru是什么,Least Recently Used,题中意思可以简单理解为最新使用的数据放在最前面,老的数据往后排,或者我们直接淘汰掉
2、根据这个思想,那么我们可以做相关的设计,哈希+链表,首先判断数据是否在我们的lrucache里面,如果在,我们就把它放在第一位,剩下的还是按照原顺序组合,如果不在,我们就淘汰最末位,将新数据放在第一位,如此即可

代码

class LRUCache {
    class DLinkedNode {
        int key, value;
        DLinkedNode prev, next;
        public DLinkedNode() {}
        public DLinkedNode(int _key, int _value) {
            key = _key;
            value = _value;
        }
    }

    private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
    private int size, cap;
    private DLinkedNode head, tail;

    public LRUCache(int capacity) {
        size = 0;
        cap = capacity;
        //add dummy head and dummyTail
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) return -1;
        //if key exist, move it to head by using its location store in Hashmap
        moveToHead(node);
        return node.value;
    }

    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            //made a newNode if it does not exist
            DLinkedNode newNode = new DLinkedNode(key, value);
            cache.put(key, newNode);
            addToHead(newNode);
            ++size;
            if (size > cap) {
                DLinkedNode removedTail = removeTail();
                cache.remove(removedTail.key);
                --size;
            }
        } else {
            node.value = value;
            moveToHead(node);
        }
    }

    private void addToHead(DLinkedNode node){
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    private void removeNode(DLinkedNode node){
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    private void moveToHead(DLinkedNode node){
        removeNode(node);
        addToHead(node);
    }

    private DLinkedNode removeTail(){
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }
}

复杂度分析

时间复杂度:O(1) 空间复杂度:O(n)

Abby-xu commented 1 year ago
class LRUCache:

    def __init__(self, capacity):
        self.dic = collections.OrderedDict()
        self.remain = capacity

    def get(self, key):
        if key not in self.dic:
            return -1
        v = self.dic.pop(key) 
        self.dic[key] = v   # set key as the newest one
        return v

    def put(self, key, value):
        if key in self.dic:    
            self.dic.pop(key)
        else:
            if self.remain > 0:
                self.remain -= 1  
            else:  # self.dic is full
                self.dic.popitem(last=False) 
        self.dic[key] = value
jackgaoyuan commented 1 year ago
type Node struct {
    Key int
    Value int
    Next *Node
    Prev *Node
}

type LRUCache struct {
    Head *Node
    Tail *Node
    M map[int]*Node
    Count int
    Capacity int
}

func Constructor(capacity int) LRUCache {
    head := &Node{}
    tail := &Node{}
    head.Next = tail
    tail.Prev = head
    return LRUCache{ Head: head, Tail: tail, M: make(map[int]*Node), Count: 0, Capacity: capacity }
}

func (this *LRUCache) removeNode(node *Node) {
    node.Prev.Next, node.Next.Prev = node.Next, node.Prev
    node.Prev, node.Next = nil, nil
}

func (this *LRUCache) insertToHead(node *Node) {
    node.Next, node.Prev = this.Head.Next, this.Head
    node.Prev.Next, node.Next.Prev = node, node
}

func (this *LRUCache) Get(key int) int {
    node, ok := this.M[key]
    if !ok {
        return -1
    }
    value := node.Value
    // move node to head
    this.removeNode(node)
    this.insertToHead(node)
    return value
}

func (this *LRUCache) Put(key int, value int) {
    node, ok := this.M[key]
    if ok { // if key exist, update node and move to head
        node.Value = value
        this.removeNode(node)
        this.insertToHead(node)
    } else { // if key not exist, create a new node and insert to head
        newNode := &Node{ Value: value, Key: key }
        if this.Count >= this.Capacity {
            lastNode := this.Tail.Prev
            this.removeNode(lastNode)
            delete(this.M, lastNode.Key)
            this.Count -= 1
        }
        this.insertToHead(newNode)
        this.M[key] = newNode
        this.Count += 1
    }
}
harperz24 commented 1 year ago
class ListNode:
    def __init__(self, key=None, val=None):
        self.key = key
        self.val = val
        self.prev = None
        self.next = None

class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {}
        self.head = ListNode()
        self.tail = ListNode()
        self.head.next = self.tail
        self.tail.prev = self.head

    def get(self, key: int) -> int:
        if key in self.cache:
            node = self.cache[key]
            self.remove(node)
            self.add_to_head(node)
            return node.val
        else:
            return -1

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            node = self.cache[key]
            self.remove(node)
        if len(self.cache) == self.capacity:
            self.remove(self.head.next)
        self.add_to_head(ListNode(key, value))       

    def remove(self, node):
        del self.cache[node.key]
        node.prev.next = node.next
        node.next.prev = node.prev

    def add_to_head(self, node):
        self.cache[node.key] = node
        pre_tail = self.tail.prev
        node.next = self.tail
        self.tail.prev = node
        pre_tail.next = node
        node.prev = pre_tail
bingzxy commented 1 year ago

思路

代码实现

class LRUCache {

    int capacity, size;
    Map<Integer, Node> map;
    Node head, tail;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<> ();
        head = new Node();
        tail = new Node();
        head.next = tail;
        tail.pre = head;
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }

        Node node = map.get(key);
        Node preNode = node.pre, nextNode = node.next;
        preNode.next = nextNode;
        nextNode.pre = preNode;

        Node headNext = head.next;
        head.next = node;
        node.next = headNext;
        headNext.pre = node;
        node.pre = head;

        return node.value;
    }

    public void put(int key, int value) {

        if (map.containsKey(key)) {
            get(key);
            map.get(key).value = value;
            return;
        }

        if (size == capacity) {
            Node last = tail.pre;
            last.pre.next = tail;
            tail.pre = last.pre;
            last.pre = null;
            last.next = null;
            map.remove(last.key);
            size--;
            put(key, value);
        } else {
            size++;
            Node first = new Node();
            first.key = key;
            first.value = value;

            Node next = head.next;
            head.next = first;
            first.pre = head;
            first.next = next;
            next.pre = first;

            map.put(key, first);
        }
    }

    class Node {
        private int key;
        private int value;
        Node pre, next;
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

复杂度分析

jmaStella commented 1 year ago

思路

doubly linked list+hashMap

代码

class LRUCache {

  Node head = new Node(0, 0), tail = new Node(0, 0);
  Map<Integer, Node> map = new HashMap();
  int capacity;

  public LRUCache(int _capacity) {
    capacity = _capacity;
    head.next = tail;
    tail.prev = head;
  }

  public int get(int key) {
    if(map.containsKey(key)) {
      Node node = map.get(key);
      remove(node);
      insert(node);
      return node.value;
    } else {
      return -1;
    }
  }

  public void put(int key, int value) {
    if(map.containsKey(key)) {
      remove(map.get(key));
    }
    if(map.size() == capacity) {
      remove(tail.prev);
    }
    insert(new Node(key, value));
  }

  private void remove(Node node) {
    map.remove(node.key);
    node.prev.next = node.next;
    node.next.prev = node.prev;
  }

  private void insert(Node node){
    map.put(node.key, node);
    Node headNext = head.next;
    head.next = node;
    node.prev = head;
    headNext.prev = node;
    node.next = headNext;
  }

  class Node{
    Node prev, next;
    int key, value;
    Node(int _key, int _value) {
      key = _key;
      value = _value;
    }
  }

复杂度 时间:O(1) 空间:O(N)

Hughlin07 commented 1 year ago

class LRUCache {

class DoubleLinkedNode{
    int key, value;
    DoubleLinkedNode prev, next;
    public DoubleLinkedNode(){}
    public DoubleLinkedNode(int _key, int _value){
        key = _key;
        value = _value;
    }
}

private Map<Integer, DoubleLinkedNode> cache = new HashMap<Integer, DoubleLinkedNode>();
private int size, cap;
private DoubleLinkedNode head, tail;

public LRUCache(int capacity) {
    size = 0;
    cap = capacity;
    head = new DoubleLinkedNode();
    tail = new DoubleLinkedNode();
    head.next = tail;
    tail.prev = head;
}

public int get(int key) {
    DoubleLinkedNode node = cache.get(key);
    if(node == null){
        return -1;
    }
    moveToHead(node);
    return node.value;
}

public void put(int key, int value) {
    DoubleLinkedNode node = cache.get(key);
    if(node == null){
        DoubleLinkedNode newNode = new DoubleLinkedNode(key, value);
        cache.put(key, newNode);
        addToHead(newNode);
        ++size;
        if(size > cap){
            DoubleLinkedNode removedTail = removeTail();
            cache.remove(removedTail.key);
            --size;
        }
    }
    else{
        node.value = value;
        moveToHead(node);
    }
}

private void addToHead(DoubleLinkedNode node){
    node.prev = head;
    node.next = head.next;
    head.next.prev = node;
    head.next = node;
}

private void removeNode(DoubleLinkedNode node){
    node.prev.next = node.next;
    node.next.prev = node.prev;
}

private void moveToHead(DoubleLinkedNode node){
    removeNode(node);
    addToHead(node);
}

private DoubleLinkedNode removeTail(){
    DoubleLinkedNode res = tail.prev;
    removeNode(res);
    return res;
}

}

Time: O(1) Space: O(N)

duke-github commented 1 year ago

思路

双端链表和hash结构记录

复杂度

时间复杂度O(1) 空间复杂度O(n)

代码

 class LRUCache {
        Map<Integer, DoubleListNode> cache;
        //ArrayList<DoubleListNode> keyList;
        int max;
        int len;
        DoubleListNode head;
        DoubleListNode tail;

        public LRUCache(int capacity) {
            cache = new HashMap<>(capacity);
            //keyList = new ArrayList<>();
            max = capacity;
            head = tail = null;
            len = 0;
        }

        public int get(int key) {
            DoubleListNode ans = cache.get(key);
            if(ans == null){
                return -1;
            }
            moveToLast(ans);
            return ans.getValue();
        }

        public void put(int key, int value) {
            DoubleListNode doubleListNode = cache.get(key);
            if (doubleListNode == null) {
                DoubleListNode temp = new DoubleListNode(key, value);
                if (max == len) {
                    DoubleListNode headTemp = head;
                    if (head == tail) {
                        head = tail = null;
                    } else {
                        head = head.getAfter();
                        head.setBefore(null);
                        headTemp.setAfter(null);
                    }
                    //keyList.remove(headTemp);
                    cache.remove(headTemp.getKey());
                    len--;
                }
                cache.put(key, temp);
                moveToLast(temp);
                len++;
               // keyList.add(temp);
            } else {
                doubleListNode.setKeyValue(key, value);
                moveToLast(doubleListNode);
            }
        }

        private void moveToLast(DoubleListNode temp) {
            //分四种情况
            //新增的 前节点为尾节点 尾节点=这个节点
            //头节点 下一个节点变为头节点 下一节点置空 前节点为尾节点 尾节点=这个节点
            //尾节点 不动
            //中间节点 记录前后节点 调转前后节点的指向 后节点置空 前节点为尾节点 尾节点=这个节点
            if (temp.getBefore() == null) {
                if (head == null) {
                    head = tail = temp;
                    return ;
                }
                if (temp.getAfter() != null) {
                    //头节点
                    head = head.getAfter();
                    head.setBefore(null);
                    temp.setAfter(null);
                }
                tail.setAfter(temp);
                temp.setBefore(tail);
                tail = temp;
            }else {
                if (temp.getAfter() != null) {
                    //中间节点
                    DoubleListNode after = temp.getAfter();
                    DoubleListNode before = temp.getBefore();
                    before.setAfter(after);
                    after.setBefore(before);
                    temp.setAfter(null);
                    tail.setAfter(temp);
                    temp.setBefore(tail);
                    tail = temp;
                }
            }
        }

        class DoubleListNode {
            private DoubleListNode before;
            private Integer key;
            private Integer value;
            private DoubleListNode after;

            public DoubleListNode(Integer key, Integer value) {
                this.key = key;
                this.value = value;
                this.before = null;
                this.after = null;
            }

            public DoubleListNode(DoubleListNode before, Integer key, Integer value, DoubleListNode after) {
                this.key = key;
                this.value = value;
                this.before = before;
                this.after = after;
            }

            public DoubleListNode getBefore() {
                return before;
            }

            public DoubleListNode getAfter() {
                return after;
            }

            public void setAfter(DoubleListNode after) {
                this.after = after;
            }

            public void setBefore(DoubleListNode before) {
                this.before = before;
            }

            public Integer getKey() {
                return key;
            }

            public Integer getValue() {
                return value;
            }

            public void setKeyValue(Integer key, Integer value) {
                this.key = key;
                this.value = value;
            }
        }
    }
wangzh0114 commented 1 year ago

思路

class LRUCache { private: unordered_map<int, DListNode> map; DListNode head; DListNode* tail; int size; int capacity;

public: LRUCache(int _capacity): capacity(_capacity), size(0){ head = new DListNode(); tail = new DListNode(); head->next = tail; tail->next = head; }

int get(int key) {
    if(!map.count(key)){
        return -1;
    }
    DListNode* node = map[key];
    moveToHead(node);
    return node->value;
}

void put(int key, int value) {
    if(!map.count(key)){
        DListNode* node = new DListNode(key, value);
        map[key] = node;
        addToHead(node);
        ++size;
        if(size > capacity){
            DListNode* rm = removeTail();
            map.erase(rm->key);
            delete rm;
            --size;
        }
    }
    else{
        DListNode* node = map[key];
        node->value = value;
        moveToHead(node);
    }
}

void addToHead(DListNode* node){
    node->prev = head;
    node->next = head->next;
    head->next->prev = node;
    head->next = node;
}

void removeNode(DListNode* node){
    node->prev->next = node->next;
    node->next->prev = node->prev;
}

void moveToHead(DListNode* node){
    removeNode(node);
    addToHead(node);
}

DListNode* removeTail(){
    DListNode* node = tail->prev;
    removeNode(node);
    return node;
}

};


----
### 复杂度分析
- TC:(1)
- SC:(N)
csthaha commented 1 year ago

代码:

/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
    this.capacity = capacity;
    this.map = new Map();
};

/** 
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {
    if(!this.map.has(key)) return -1;
    const v = this.map.get(key);
    //删掉再新加。
    this.map.delete(key);
    this.map.set(key, v);
    return v;
};

/** 
 * @param {number} key 
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
    // 需要先判断是否存在。因为如果是满的情况且更新值的情况会删错。
    if(this.map.get(key)) {
        // 存在先删除
        this.map.delete(key)
    }
    if(this.map.size >= this.capacity) {
        // 删除最久未使用
        // next().value 返回的是第一组键 
        this.map.delete(this.map.keys().next().value);
    }
    this.map.set(key, value)

};

/**
 * Your LRUCache object will be instantiated and called as such:
 * var obj = new LRUCache(capacity)
 * var param_1 = obj.get(key)
 * obj.put(key,value)
 */
LIMBO42 commented 1 year ago
class LRUCache {
struct Node{
    int key;
    int val;
    Node* prev;
    Node *next;
    Node() {

    }
    Node(int k, int v) : key(k), val(v) {

    }
};
unordered_map<int, Node*> m;
Node* head = nullptr;
Node* tail = nullptr;
int capacity_;

void addHead(Node* node) {
    head->next->prev = node;
    node->next = head->next;
    node->prev = head;
    head->next = node;
}
void deleteNode(Node* node) {
    node->prev->next = node->next;
    node->next->prev = node->prev;
}
public:
    LRUCache(int capacity) {
        capacity_ = capacity;
        head = new Node;
        tail = new Node;
        head->next = tail;
        tail->next = head;
        head->prev = tail;
        tail->prev = head;
    }

    int get(int key) {
        if(m.count(key)) {
            int res = m[key]->val;
            Node *node = m[key];
            deleteNode(node);
            addHead(node);
            return res;
        }
        return -1;
    }

    void put(int key, int value) {
        if(m.count(key)) {
            Node* node = m[key];
            node->val = value;
            deleteNode(node);
            addHead(node);
            return;
        }
        Node* node = new Node(key, value);
        addHead(node);
        m[key] = node;
        if(m.size() > capacity_) {
            Node* last = tail->prev;
            m.erase(last->key);
            deleteNode(last);
        }
    }
};
Jetery commented 1 year ago

146. LRU 缓存

思路

list来维护关键字的新旧, map来记录关键字的键值对

小技巧: 由于put时也会对元素新旧产生影响, 可以在put时调用get

代码 (Java)

class LRUCache {

    private HashMap<Integer, Integer> map;
    private ArrayList<Integer> list;
    private int max;

    public LRUCache(int capacity) {
        max = capacity;
        map = new HashMap<>();
        list = new ArrayList<>();
    }

    public int get(int key) {
        if (map.containsKey(key)) {
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i) == key) {
                    list.remove(i);
                    list.add(key);
                    break;
                }
            }
            return map.get(key);
        }
        else return -1;
    }

    public void put(int key, int value) {
        if (this.get(key) == -1) {
            if (list.size() == max) {
                int rm = list.get(0);
                list.remove(0);
                map.remove(rm);
            }
            list.add(key);
        }

        map.put(key, value);
    }
}

复杂度分析

ccslience commented 1 year ago
class LRUCache:

    def __init__(self, capacity: int):
        self.cache = OrderedDict()
        self.length = 0
        self.capacity = capacity

    def get(self, key: int) -> int:
        if key in self.cache:
            self.cache.move_to_end(key)
            return self.cache[key]
        return -1

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            self.cache[key] = value
            self.cache.move_to_end(key)
        else:
            if self.length < self.capacity:
                self.length += 1
            else:
                self.cache.popitem(False)
            self.cache[key] = value
Horace7 commented 1 year ago
/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
    this.size = capacity;
    this.map = new Map();
};

/**
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {
    let val = -1;
    if (this.map.has(key)) {
        val = this.map.get(key);
        this.map.delete(key);
        this.map.set(key, val);
    }
    return val;
};

/**
 * @param {number} key
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
    if (this.map.has(key)) {
        this.map.delete(key);
    }
    this.map.set(key, value);
    if (this.map.size > this.size) {
        this.map.delete(this.map.keys().next().value);
    }
};
xb798298436 commented 1 year ago

var LRUCache = function(capacity) { this.size = capacity; this.map = new Map(); };

LRUCache.prototype.get = function(key) { let val = -1; if (this.map.has(key)) { val = this.map.get(key); this.map.delete(key); this.map.set(key, val); } return val; };

LRUCache.prototype.put = function(key, value) { if (this.map.has(key)) { this.map.delete(key); } this.map.set(key, value); if (this.map.size > this.size) { this.map.delete(this.map.keys().next().value); } };

joemonkeylee commented 1 year ago

思路

需要一个hash表 和双向链表

代码


        public class LRUCache
        {
            Dictionary<int, int> dict;
            LinkedList<int> nums;
            int capacity;

            public LRUCache(int capacity)
            {
                this.capacity = capacity;
                dict = new Dictionary<int, int>();
                nums = new LinkedList<int>();
            }

            public int Get(int key)
            {
                if (dict.ContainsKey(key))
                {
                    nums.Remove(key);
                    nums.AddLast(key);
                    return dict[key];
                }
                return -1;
            }

            public void Put(int key, int value)
            {
                if (dict.ContainsKey(key))
                {
                    nums.Remove(key);
                    nums.AddLast(key);
                    dict[key] = value;
                }
                else
                {
                    if (nums.Count == capacity)
                    {
                        dict.Remove(nums.First.Value);
                        nums.RemoveFirst();
                        nums.AddLast(key);
                        dict.Add(key, value);
                    }
                    else
                    {
                        nums.AddLast(key);
                        dict.Add(key, value);
                    }
                }
            }
        }
jiujingxukong commented 1 year ago

解题思路

这个题目太好了。

  1. 增删改查(get是查,put包含增删改查)要求时间复杂度是O(1) 考虑增删操作时间复杂度是O(1),所以用链表(链表和数组中选择)。
    考虑查改操作时间复杂度是O(1),所以用哈希表来辅助,以空间换时间。
  2. LRU 缓存机制要求逐出最久未使用的关键字,恰好符合链表插入和删除节点有序的特点。
  3. node节点和node.value是不同的,node节点指node在内存中的物理存储。
  4. 移除链表节点后还需要把该节点前后的两个节点连起来,因此我们需要的是双向链表而不是单向链表。

算法思路

// put

if key 存在:
    更新节点值
    把节点移到链表头部

else:
    if 缓存满了:
        移除最后一个节点
        删除它在哈希表中的映射

    新建一个节点
    把节点加到链表头部
    在哈希表中增加映射

// get

if key 存在:
    返回节点值
    把节点移到链表头部
else:
    返回 -1

代码(答案里的代码,我写的不知道哪里错了)

class DoubleLinkedListNode {
    constructor(key, value) {
        this.key = key
        this.value = value
        this.prev = null
        this.next = null
    }
}

class LRUCache {
    constructor(capacity) {
        this.capacity = capacity
        this.usedSpace = 0
        // Mappings of key->node.
        this.hashmap = {}
        this.dummyHead = new DoubleLinkedListNode(null, null)
        this.dummyTail = new DoubleLinkedListNode(null, null)
        this.dummyHead.next = this.dummyTail
        this.dummyTail.prev = this.dummyHead
    }

    _isFull() {
        return this.usedSpace === this.capacity
    }

    _removeNode(node) {
        node.prev.next = node.next
        node.next.prev = node.prev
        node.prev = null
        node.next = null
        return node
    }

    _addToHead(node) {
        const head = this.dummyHead.next
        node.next = head
        head.prev = node
        node.prev = this.dummyHead
        this.dummyHead.next = node
    }

    get(key) {
        if (key in this.hashmap) {
            const node = this.hashmap[key]
            this._addToHead(this._removeNode(node))
            return node.value
        }
        else {
            return -1
        }
    }

    put(key, value) {
        if (key in this.hashmap) {
            // If key exists, update the corresponding node and move it to the head.
            const node = this.hashmap[key]
            node.value = value
            this._addToHead(this._removeNode(node))
        }
        else {
        // If it's a new key.
            if (this._isFull()) {
                // If the cache is full, remove the tail node.
                const node = this.dummyTail.prev
                delete this.hashmap[node.key]
                this._removeNode(node)
                this.usedSpace--
            }
            // Create a new node and add it to the head.
            const node = new DoubleLinkedListNode(key, value)
            this.hashmap[key] = node
            this._addToHead(node)
            this.usedSpace++
        }
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * var obj = new LRUCache(capacity)
 * var param_1 = obj.get(key)
 * obj.put(key,value)
 */

复杂度分析

tzuikuo commented 1 year ago

思路

待定

代码

class LRUCache
{
    public:
        list<pair<int,int>> l;
        unordered_map<int,list<pair<int, int>>::iterator> m;
        int size;
        LRUCache(int capacity)
        {
            size=capacity;
        }
        int get(int key)
        {
            if(m.find(key)==m.end())
                return -1;
            l.splice(l.begin(),l,m[key]);
            return m[key]->second;
        }
        void put(int key, int value)
        {
            if(m.find(key)!=m.end())
            {
                l.splice(l.begin(),l,m[key]);
                m[key]->second=value;
                return;
            }
            if(l.size()==size)
            {
                auto d_key=l.back().first;
                l.pop_back();
                m.erase(d_key);
            }
            l.push_front({key,value});
            m[key]=l.begin();
        }
};

复杂度分析 待定

Lydia61 commented 1 year ago

146. LRU

思路

经典LRU算法

代码

class LRUCache(object):

    def __init__(self, capacity):
        """
        :type capacity: int
        """
        LRUCache.capacity = capacity
        LRUCache.length = 0
        LRUCache.dict = collections.OrderedDict()

    def get(self, key):
        """
        :rtype: int
        """
        try:
            value = LRUCache.dict[key]
            del LRUCache.dict[key]
            LRUCache.dict[key] = value
            return value
        except:
            return -1

    def put(self, key, value):
        """
        :type key: int
        :type value: int
        :rtype: nothing
        """
        try:
            del LRUCache.dict[key]
            LRUCache.dict[key] = value
        except:
            if LRUCache.length == LRUCache.capacity:
                LRUCache.dict.popitem(last = False)
                LRUCache.length -= 1
            LRUCache.dict[key] = value
            LRUCache.length += 1

复杂度分析

kangliqi1 commented 1 year ago

class LRUCache {

private HashMap<Integer, Integer> map;
private ArrayList<Integer> list;
private int max;

public LRUCache(int capacity) {
    max = capacity;
    map = new HashMap<>();
    list = new ArrayList<>();
}

public int get(int key) {
    if (map.containsKey(key)) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i) == key) {
                list.remove(i);
                list.add(key);
                break;
            }
        }
        return map.get(key);
    }
    else return -1;
}

public void put(int key, int value) {
    if (this.get(key) == -1) {
        if (list.size() == max) {
            int rm = list.get(0);
            list.remove(0);
            map.remove(rm);
        }
        list.add(key);
    }

    map.put(key, value);
}

}

JadeLiu13 commented 1 year ago

毫无思路,看评论需要补充下LRU基础

class LRUCache:

def __init__(self, capacity: int):
    self.cache = OrderedDict()
    self.cap = capacity

def get(self, key: int) -> int:
    if key in self.cache:
        self.cache.move_to_end(key)
        return self.cache[key]
    return -1

def put(self, key: int, value: int) -> None:
    self.cache[key] = value
    self.cache.move_to_end(key)
    if len(self.cache)>self.cap:
        self.cache.popitem(last=False)
uratora commented 1 year ago

思路

为使两个函数达到O(1)复杂度,同时使用链表和哈希表

代码

struct ListNodes {
    ListNodes* prev;
    ListNodes* next;
    int key, val;
    ListNodes(): key(0), val(0), prev(nullptr), next(nullptr){}
    ListNodes(int _key, int _value): key(_key), val(_value), prev(nullptr), next(nullptr){}
};

class LRUCache {
private:
    unordered_map<int, ListNodes*> hashmap;
    ListNodes* head;
    ListNodes* tail;
    int capacity, size;

public:
    LRUCache(int _capacity): capacity(_capacity), size(0) {
        head = new ListNodes();
        tail = new ListNodes();
        head->next = tail;
        tail->prev = head;
    }

    int get(int key) {
        if(!hashmap.count(key))
            return -1;
        ListNodes* node = hashmap[key];
        moveTohead(node);
        return node->val;
    }

    void put(int key, int value) {
        if(!hashmap.count(key)){
            ListNodes* node = new ListNodes(key, value);
            hashmap[key] = node;
            addTohead(node);
            ++size;
            if(size > capacity){
                ListNodes* removed = removeTail();
                hashmap.erase(removed->key);
                --size;
                delete removed;
            }
        }
        else {
            ListNodes* node = hashmap[key];
            node->val = value;
            moveTohead(node);
        }
    }

    void addTohead(ListNodes* node){
        node->prev = head;
        node->next = head->next;
        head->next->prev = node;
        head->next = node;
    }

    void removeNode(ListNodes* node){
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }

    void moveTohead(ListNodes* node){
        removeNode(node);
        addTohead(node);
    }

    ListNodes* removeTail(){
        ListNodes* node = tail->prev;
        removeNode(node);
        return node;
    }
};

复杂度

时间:O(1) 空间:O(N)

Elsa-zhang commented 1 year ago
class LRUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.dict = OrderedDict()

    def get(self, key: int) -> int:
        if key not in self.dict:
            return -1
        self.dict.move_to_end(key)
        return self.dict[key]

    def put(self, key: int, value: int) -> None:
        if key in self.dict:
            self.dict.move_to_end(key)
        self.dict[key] = value

        if len(self.dict) > self.capacity:
            self.dict.popitem(last=False)
Elon-Lau commented 1 year ago

class LRUCache:

def __init__(self, capacity: int):
    self.capacity = capacity
    self.dict = OrderedDict()

def get(self, key: int) -> int:
    if key not in self.dict:
        return -1
    self.dict.move_to_end(key)
    return self.dict[key]

def put(self, key: int, value: int) -> None:
    if key in self.dict:
        self.dict.move_to_end(key)
    self.dict[key] = value

    if len(self.dict) > self.capacity:
        self.dict.popitem(last=False)
liuajingliu commented 1 year ago

解题思路

采用哈希表,保证get操作在O(1)时间复杂度内完成,
构建双向链表,保证put操作在O(1)时间复杂度内完成

代码实现

javaScript

var LinkedList = function(key, val) {
    this.key = key;
    this.val = val;
    this.prev = null;
    this.next = null;
}

/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
    this.capacity = capacity;
    this.size = 0;
    this.cache = new Map();
    // 构建虚拟节点
    this.dummyHead = new LinkedList();
    this.dummyTail = new LinkedList();
    this.dummyHead.next = this.dummyTail;
    this.dummyTail.prev = this.dummyHead;
};

/** 
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {
    if (!this.cache.has(key)) {
        return -1;
    }
    const node = this.cache.get(key);
    // 将节点移动至链表头部
    this.removeNode(node);
    this.appendToHead(node);
    return node.val;
};

/** 
 * @param {number} key 
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
    // 判断关键字key是否存在于缓存中
    const node = this.cache.get(key);
    if (node) {
        // 更新关键字的值
        node.val = value;
        this.cache.set(key, node);
        // 将关键字移动至链表头部
        this.removeNode(node);
        this.appendToHead(node);
    } else {
        // 当缓存容量达到上限时
        if (this.size === this.capacity) {
            // 删除最久未使用的
            this.removeTailNode();
        }
        // 创建新节点
        const newNode = new LinkedList(key, value);
        this.cache.set(key, newNode)
        this.appendToHead(newNode);
        this.size ++;
    }
};

LRUCache.prototype.removeNode = function(node) {
    let preNode = node.prev;
    let nextNode = node.next;
    preNode.next = nextNode;
    nextNode.prev = preNode;
}

LRUCache.prototype.appendToHead = function(node) {
    let head = this.dummyHead.next;
    this.dummyHead.next = node;
    node.prev = this.dummyHead;
    node.next = head;
    head.prev = node;
}

LRUCache.prototype.removeTailNode = function() {
    this.size --;
    let tailNode = this.dummyTail.prev;
    this.cache.delete(tailNode.key);
    this.removeNode(tailNode);
}

复杂度分析

for123s commented 1 year ago
struct DLinkeNode{
    int key, value;
    DLinkeNode* prev;
    DLinkeNode* next;
    DLinkeNode(): key(0), value(0), prev(nullptr), next(nullptr){}
    DLinkeNode(int key1, int value1): key(key1), value(value1), prev(nullptr), next(nullptr){}
};

class LRUCache {
private:
    unordered_map<int,DLinkeNode*> mp;
    DLinkeNode* head;
    DLinkeNode* tail;
    int max_capacity;
    int size;

public:
    void removeNode(DLinkeNode* node)
    {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }

    void addToHead(DLinkeNode* node)
    {
        node->next = head->next;
        head->next = node;
        node->prev = head;
        node->next->prev = node;
    }

    void moveToHead(DLinkeNode* node)
    {
        removeNode(node);
        addToHead(node);
    }

    LRUCache(int capacity) {
        mp.clear();
        max_capacity = capacity;
        size = 0;
        head = new DLinkeNode();
        tail = new DLinkeNode();
        head->next = tail;
        tail->prev = head;
    }

    int get(int key) {
        if(mp.count(key)==0)
            return -1;
        DLinkeNode* node = mp[key];
        moveToHead(node);
        return node->value;
    }

    void put(int key, int value) {
        if(mp.count(key)==0)
        {
            DLinkeNode* node = new DLinkeNode(key,value);
            mp[key] = node;
            addToHead(node);
            ++size;
            if(size>max_capacity)
            {
                DLinkeNode* temp = tail->prev;
                tail->prev = temp->prev;
                temp->prev->next = tail;
                mp.erase(temp->key);
                delete temp;
                --size;
            }
        }
        else
        {
            DLinkeNode* node = mp[key];
            node->value = value;
            moveToHead(node);
        }
    }
};
Aneureka commented 1 year ago

Below is my C++ solution using std::unordered_map and std::list. Hope it helps ;-)

#include <unordered_map>
#include <list>

class LRUCache {
public:
    LRUCache(int capacity) : capacity(capacity) {}

    int get(int key) {
        auto node = m.find(key);
        if (node == m.end()) return -1;

        // put the retrieved node to last
        auto itr = node->second;
        int value = itr->second;
        nodes.erase(itr);
        nodes.push_back({key, value});
        m[key] = prev(nodes.end());

        return value;
    }

    void put(int key, int value) {
        // if key is existed, remove it from map
        if (auto node = m.find(key); node != m.end()) {
            nodes.erase(node->second);
        }

        // if capacity is reached, remove the first-to-kill node
        if (nodes.size() >= capacity) {
            int key_to_remove = nodes.front().first;
            m.erase(m.find(key_to_remove));
            nodes.pop_front();
        }

        // add new node
        nodes.push_back({key, value});
        m[key] = prev(nodes.end());
    }
private:
    using PII = pair<int, int>;
    unordered_map<int, list<PII>::iterator> m; // store key -> ref to node
    list<PII> nodes; // store node{key, value}
    int capacity;
};
texamc2 commented 1 year ago

type entry struct { key, value int }
type LRUCache struct {
    cap   int
    cache map[int]*list.Element
    lst   *list.List
}

func Constructor(capacity int) LRUCache {
    return LRUCache{capacity, map[int]*list.Element{}, list.New()}
}

func (c *LRUCache) Get(key int) int {
    e := c.cache[key]
    if e == nil { return -1 }
    c.lst.MoveToFront(e) // 刷新缓存使用时间
    return e.Value.(entry).value
}

func (c *LRUCache) Put(key, value int) {
    if e := c.cache[key]; e != nil {
        e.Value = entry{key, value}
        c.lst.MoveToFront(e) // 刷新缓存使用时间
        return
    }
    c.cache[key] = c.lst.PushFront(entry{key, value})
    if len(c.cache) > c.cap {
        delete(c.cache, c.lst.Remove(c.lst.Back()).(entry).key)
    }
}
DragonFCL commented 1 year ago
var LRUCache = function(capacity) {
    this.cap = capacity;
    this.cache = {};
    this.length = 0;
};
LRUCache.prototype.get = function(key) {
    if(this.cache[key] != undefined) {
        var temp = this.cache[key];
        delete this.cache[key];
        console.log(this.cache); 
        this.cache[key] = temp;

        return temp;
    } else {
        return -1;
    } 

};

LRUCache.prototype.put = function(key, value) {
    if(this.length < this.cap && this.cache[key] == undefined) {
        this.cache[key] = value;
        this.length++;
    } else if(this.length == this.cap && this.cache[key] == undefined) {
        var arr = Object.keys(this.cache);
        delete this.cache[arr[0]];
        this.cache[key] = value;
    }
    console.log(this.cache);
};
linlizzz commented 1 year ago

思路

1.利用collections库中已有的记住数据添加顺序的字典子类OrderedDic()来储存

2.每操作一次键值对,就pop一次再录入一次

3.函数popitem(last=)当参数last=True时是栈LIFO,last=False时是队列FIFO,此处‘最近最少使用’是队列FIFO特征,则用last=False

代码

import collections

class LRUCache:

    def __init__(self, capacity: int):
        self.cache = collections.OrderedDict()
        self.size = capacity

    def get(self, key: int) -> int:
        if key in self.cache:
            res = self.cache.pop(key)
            self.cache[key] = res
        else:
            res = -1
        return res

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            self.cache.pop(key)
            self.cache[key] = value
        elif len(self.cache) == self.size:
            self.cache.popitem(last=False)
        self.cache[key] = value

复杂度分析

T(n) = O(1)

S(n) = O(n),n为capacity

ZhuMengCheng commented 1 year ago
/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
    this.map = new Map();
    this.capacity = capacity;
};

/** 
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {
    if(this.map.has(key)){
        let value = this.map.get(key)
        this.map.delete(key)
        this.map.set(key, value)
        return value
    }
    return -1

};

/** 
 * @param {number} key 
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
 if (this.map.has(key)) { 
      this.map.delete(key) 
    }
    this.map.set(key, value)
    if(this.map.size > this.capacity){
        this.map.delete(this.map.keys().next().value)
    }

};

复杂度分析 时间复杂度:O(1) 空间复杂度:O(N)

luckyoneday commented 1 year ago

代码

/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
  this.map = new Map();
  this.size = capacity;
};

/** 
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {
    let val = this.map.get(key)
    if (val !== undefined) {
        this.map.delete(key)
        this.map.set(key, val);
        return val
    }

     return -1;
};

/** 
 * @param {number} key 
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
    if (this.map.has(key)) {
        this.map.delete(key);
    } else if (this.map.size >= this.size) {
        const k = this.map.keys().next().value;
        this.map.delete(k);
    }

    this.map.set(key, value);
};
954545647 commented 1 year ago

思路

双向链表+hashmap

代码

class DoubleLinkedListNode {
  constructor(key,value) {
    this.key = key;
    this.value = value;
    this.prev = null;
    this.next = null;
  }
}
class LRUCache {
  constructor(capacity){
    this.capacity = capacity;
    this.hashmap = {}
    this.useSize = 0;
    this.dummyHead= new DoubleLinkedListNode(null,null);
    this.dummyTail= new DoubleLinkedListNode(null,null);
    this.dummyHead.next = this.dummyTail;
    this.dummyTail.prev = this.dummyHead;
  }

  _isFull(){
    return this.useSize === this.capacity;
  }

  _removeNode(node){
    node.prev.next = node.next;
    node.next.prev = node.prev;
    node.prev = null;
    node.next = null;
    return node;
  }

  _addToHead(node){
    const head = this.dummyHead.next;
    node.next = head;
    node.prev = this.dummyHead;
    this.dummyHead.next = node;
    head.prev = node;
  }

  get(key){
    if(this.hashmap[key]){
      const node = this.hashmap[key];
      const removeNode = this._removeNode(node);
      this._addToHead(removeNode);
      return node.value;
    }else{
      return -1;
    }
  }

  put(key,val){
    if(this.hashmap[key]){
      const node = this.hashmap[key];
      node.value = val;
      this._addToHead(this._removeNode(node)) 
    }else{
      if(this._isFull()){
        // 先获取最后一个
        const node = this.dummyTail.prev;
        this._removeNode(node);
        delete this.hashmap[node.key];
        this.useSize--;
      }

      const node = new DoubleLinkedListNode(key,val);
      this.hashmap[key] = node;
      this._addToHead(node)
      this.useSize++
    }
  }
}

复杂度分析

DrinkMorekaik commented 1 year ago

使用Map实现,每次get和put时需要删除原有值再设置,以保证map是按修改的顺序依次排序的


class LRUCache {
    constructor (opacity) {
        this.size = opacity
        this._storage = new Map()
    }
    get (key) {
        if (this._storage.has(key)) {
            const value = this._storage.get(key)
            this._storage.delete(key)
            this._storage.set(key, value)
            return value
        } else {
            return -1
        }
    }
    put (key, value) {
        this._storage.delete(key)
        this._storage.set(key, value)
        if (this._storage.size > this.size) {
            this._storage.delete(this._storage.keys().next().value)
        }
    }
}
JasonQiu commented 1 year ago

class LRUCache: def init(self, capacity: int): self.capacity = capacity self.hashmap = {} self.head = ListNode(0, 0) self.tail = ListNode(0, 0) self.head.next = self.tail self.tail.prev = self.head

def insert(self, node: ListNode):
    prev = self.tail.prev
    prev.next = node
    node.prev = prev
    node.next = self.tail
    self.tail.prev = node

def remove(self, node: ListNode):
    prev = node.prev
    next = node.next
    prev.next = next
    next.prev = prev

def get(self, key: int) -> int:
    if key in self.hashmap:
        self.remove(self.hashmap[key])
        self.insert(self.hashmap[key])
        return self.hashmap[key].val
    else:
        return -1

def put(self, key: int, value: int) -> None:
    if key in self.hashmap:
        self.remove(self.hashmap[key])
    self.hashmap[key] = ListNode(key, value)
    self.insert(self.hashmap[key])

    if len(self.hashmap) > self.capacity:
        lru = self.head.next
        self.remove(lru)
        del self.hashmap[lru.key]

Time: O(1)
Space: O(n)
zcytm3000 commented 1 year ago

class LRUCache(collections.OrderedDict):

def __init__(self, capacity: int):
    super().__init__()
    self.capacity = capacity

def get(self, key: int) -> int:
    if key not in self:
        return -1
    self.move_to_end(key)
    return self[key]

def put(self, key: int, value: int) -> None:
    if key in self:
        self.move_to_end(key)
    self[key] = value
    if len(self) > self.capacity:
        self.popitem(last=False)
zpbc007 commented 1 year ago

思路

使用 map 存储节点信息,使用链表存储节点访问情况

代码

import { ListNode } from "@utils/list-node"

export class LRUCache {
    private dataNodeMap = new Map<number, ListNode<{key: number, val: number}>>()
    private outdateLink = new ListNode<{key: number, val: number}>()

    constructor(private readonly capacity: number) {}

    get(key: number): number {
        if (!this.dataNodeMap.has(key)) {
            return -1
        }
        const node = this.dataNodeMap.get(key)!
        this.updateNode(node)

        return node.val.val
    }

    put(key: number, value: number): void {
        // 更新逻辑
        if (this.dataNodeMap.has(key)) {
            const node = this.dataNodeMap.get(key)!
            node.val.val = value
            this.updateNode(node)
        } else { // 插入逻辑
            const node = new ListNode<{key: number, val: number}>({key, val: value})
            this.dataNodeMap.set(key, node)
            node.next = this.outdateLink.next
            this.outdateLink.next = node

            this.removeTailNode()
        }
    }

    // 删除过期的尾节点
    private removeTailNode() {
        let index = 0
        let cur = this.outdateLink
        while (cur) {
            if (index > this.capacity) {
                this.dataNodeMap.delete(cur.val.key)
                cur.next = null
            }
            cur = cur.next!
            index++
        }
    }

    private updateNode(node: ListNode<{key: number, val: number}>) {
        let pre: ListNode<{key: number, val: number}> = this.outdateLink    
        let cur: ListNode<{key: number, val: number}> = this.outdateLink.next!

        while (cur !== node) {
            pre = cur
            cur = cur.next!
        }

        pre.next = cur.next
        cur.next = this.outdateLink.next
        this.outdateLink.next = cur
    }
}

复杂度分析

备注

目前是使用单向链表实现的,导致 get put 复杂度都不是 O(1),使用双向链表即可进行优化