leetcode刷題之LRU緩存


LRU Cache

LRU Cache介紹

關於此緩存機制的介紹,參考國外大佬的文章

LRU Cache(Least Recently Used cache)是最流行的緩存驅逐策略之一。同時也是一個非常常見的面試問題。

LRU 策略

LRU Cache會跟蹤緩存中的項目的被訪問順序。按照請求順序存儲項目。因此,最近使用的項目將位於緩存的頂部,而最近最少用的項目將位於緩存的尾部。在LRU的策略之中,當緩存已滿時,最長時間未使用的項目將被淘汰逐出緩存。

依照上述。LRU緩存的原理、思路其實很簡單,如果緩存的容量是n,意味着最近使用的n個或n個以內的項目存儲於該緩存中。當緩存占滿,每次添加新項目,都會把最末尾的項目刪去,再往頂部放入最新項目。

舉例

為了更加方便的看出對緩存的數據的儲存以及淘汰等操作,舉例說明

首先給定容量為3的一個LRU緩存空間,然后依次請求訪問也即壓入數據A、B、C。我們可以看到緩存的變化。緩存從頂至底放入數據,由於C最后放入,所以位於緩存最頂部。

image-20220109222252494

接着,請求訪問B,B便會被至於緩存的頂部,表示最近訪問

image-20220109222555561

接着是請求訪問D,於是緩存就放入了數據D,在放入之前要把最底端的數據移除,即A移除,以此保證LRU容量的恆定。

image-20220109222822576

LRU的工作原理上面例子就清楚了,接下來看如何實現

實現LRU Cache

image-20220109223445163

對於LRU緩存的實現,就是leetcode上的這道題。

說一下思路:

題目談到key-value(關鍵字和值)的存在,讓我們想起了java的hashmap和python的dict

都是以鍵值對的形式儲存

先說hashmap,由於hashmap無法記錄訪問數據項目訪問的前后順序,這里考慮使用雙鏈表來記錄順序。

用鏈表不用數組是因為對數據修改的時間復雜度為O(n)

使用hashmap和雙鏈表結合就避免了這個問題。

代碼如下:


class Node {
    int key;
    int val;
    Node prev;
    Node next;
    public Node(int key,int value) {
        this.key = key;
        this.val = value;
    }
}
class LRUCache{
    int capacity;
    Map<Integer,Node> map = new HashMap<>();
    Node head = new Node(0, 0);
    Node tail = new Node(0, 0);
    public LRUCache(int capacity) {
        this.capacity = capacity;
        head.next = tail;
        tail.prev = head;

    }
    
    public void put(int key,int value) {
        if (!map.containsKey(key)) {
            if (map.size() == capacity) deletLastNode();
            	Node newNode = new Node(key, value);
            	newNode.prev = head;
            	newNode.next = head.next;
            	head.next.prev = newNode;
            	head.next = newNode;
            	map.put(key,newNode);
        }else {
            Node node = map.get(key);
            node.val = value;
            moveNodeToTop(node);
        }
    }
    
    public int get(int key) {
         if (map.containsKey(key)) {
             Node node = map.get(key);
             moveNodeToTop(node);
             return node.val;
         }else {
             return -1;
         }
     } 
    
    private void deletLastNode() {
        Node node = tail.prev;
        node.prev.next = tail;
        tail.prev = node.prev;
        map.remove(node.key);
    }
    
    private void moveNodeToTop(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    } 
}

python就不需要像這樣用哈希表和鏈表結合,python有字典而且是有序字典

用orderedDict()作為LRU緩沖區

利用orderedDict()的特殊方法popitem()可以實現對最前或者最后數據的操作

popitem()的選項last為ture時表示的是棧狀態,把剛放入的pop出去;last為false表示的是隊列的狀態,把最早放入的pop出去

from collections import OrderedDict
class LRUCache:
    def __init__(self, capacity:int):
        self.capacity = capacity
        self.cache = OrderedDict()
        self.size = 0

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

    def put(self, key:int, value:int) ->None:
        if key in self.cache:
            del self.cache[key]
            self.cache[key] = value
        elif self.size >= self.capacity: 
            self.cache.popitem(last=False)
        else:
            self.size +=1
        self.cache[key] = value
        


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM