1. 程式人生 > 實用技巧 >【力扣】146. LRU快取機制

【力扣】146. LRU快取機制

運用你所掌握的資料結構,設計和實現一個 LRU (最近最少使用) 快取機制。它應該支援以下操作: 獲取資料 get 和 寫入資料 put 。

獲取資料 get(key) - 如果關鍵字 (key) 存在於快取中,則獲取關鍵字的值(總是正數),否則返回 -1。
寫入資料 put(key, value) - 如果關鍵字已經存在,則變更其資料值;如果關鍵字不存在,則插入該組「關鍵字/值」。當快取容量達到上限時,它應該在寫入新資料之前刪除最久未使用的資料值,從而為新的資料值留出空間。

進階:

你是否可以在O(1) 時間複雜度內完成這兩種操作?

示例:

LRUCache cache = new LRUCache( 2 /* 快取容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 該操作會使得關鍵字 2 作廢
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 該操作會使得關鍵字 1 作廢
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/lru-cache

class LRUCache {

//LRU 快取機制可以通過雜湊表輔以雙向連結串列實現,我們用一個雜湊表和一個雙向連結串列維護所有在快取中的鍵值對。
    private static class Node{
        private int key;

        private int value;

        private Node prev; //前置結點

        private Node next; //下一個節點

        public Node(){}

        public Node(int key,int value){
            
this.key = key; this.value = value; } } //當容量滿的時候,採用LRU淘汰策略 //1.淘汰最少使用的資料 //2.淘汰最早放進來的資料 private Map<Integer,Node> map = new HashMap<Integer,Node>(); //標識容量 是一個固定值 private int capacity; //標識大小,當前大小 private int size; //頭結點 private Node head; //尾節點 private Node tail; public LRUCache(int capacity) { this.capacity = capacity; this.size = 0; // 使用偽頭部和偽尾部節點 head = new Node(); tail = new Node(); head.next = tail; tail.prev = head; } //如果 key 不存在,則返回 -1−1; //如果 key 存在,則 key 對應的節點是最近被使用的節點。通過雜湊表定位到該節點在雙向連結串列中的位置,並將其移動到雙向連結串列的頭部,最後返回該節點的值 public int get(int key) { Node result = map.get(key); if(result == null){ return -1; } //通過雜湊表定位到該節點在雙向連結串列中的位置,並將其移動到雙向連結串列的頭部 result.prev.next = result.next; result.next.prev = result.prev; //把當前節點放到頭結點 result.prev = head; result.next = head.next; head.next.prev = result; head.next = result; return result.value; } /** 對於 put 操作,首先判斷 key 是否存在: 如果 key 不存在,使用 key 和 value 建立一個新的節點,在雙向連結串列的頭部新增該節點,並將 key 和該節點新增進雜湊表中。然後判斷雙向連結串列的節點數是否超出容量,如果超出容量,則刪除雙向連結串列的尾部節點,並刪除雜湊表中對應的項; 如果 key 存在,則與 get 操作類似,先通過雜湊表定位,再將對應的節點的值更新為 value,並將該節點移到雙向連結串列的頭部。 **/ public void put(int key, int value) { Node result = map.get(key); //若是沒有則放入新的node節點 if(result == null){ Node node = new Node(key,value); //新增到hash表 map.put(key,node); //把新加入的節點放到連結串列的頭部 node.prev = head; node.next = head.next; head.next.prev = node; head.next = node; ++size; //如果當前大小已經超出了容量,就把最久未使用的刪除 if(size > capacity){ //去除hash表中的值 map.remove(tail.prev.key); //去除連結串列中最久未使用的節點 tail.prev.prev.next = tail; tail.prev = tail.prev.prev; --size; } } else { //若是有值,需要把原來的值覆蓋 result.value = value; map.put(key,result); //把連結串列中的節點放到頭結點 //先把當前節點刪除 result.prev.next = result.next; result.next.prev = result.prev; //把當前節點放到頭結點 result.prev = head; result.next = head.next; head.next.prev = result; head.next = result; } } } /** * 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); */