力扣(LeetCode) -146 LRU緩存機制

本題考察的LRU緩存機制,HashMap和鏈表

題目描述

運用你所掌握的數(shù)據(jù)結(jié)構(gòu),設(shè)計和實現(xiàn)一個 LRU (最近最少使用) 緩存機制。它應(yīng)該支持以下操作: 獲取數(shù)據(jù) get 和 寫入數(shù)據(jù) put

獲取數(shù)據(jù) get(key) - 如果密鑰 (key) 存在于緩存中,則獲取密鑰的值(總是正數(shù)),否則返回 -1。
寫入數(shù)據(jù) put(key, value) - 如果密鑰不存在,則寫入其數(shù)據(jù)值。當(dāng)緩存容量達到上限時,它應(yīng)該在寫入新數(shù)據(jù)之前刪除最近最少使用的數(shù)據(jù)值,從而為新的數(shù)據(jù)值留出空間。

進階:
你是否可以在 O(1) 時間復(fù)雜度內(nèi)完成這兩種操作?

示例:

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

題目思考

首先我們應(yīng)該了解LRU算法的原理。LRU算法總結(jié)來說就是:一個數(shù)據(jù)很久沒有訪問,那么這個數(shù)據(jù)在以后也很少訪問。對于這樣的很久沒有訪問的數(shù)據(jù),如果緩存空間不足,就把那些很久沒有使用的數(shù)據(jù)清除。

那怎么樣實現(xiàn)這樣一種算法呢?我們可以為每一個數(shù)據(jù)標(biāo)記一個時間戳,標(biāo)記了最近一次使用的時間,然后不斷更新時間戳,緩存沒用空間的時候刪除距離現(xiàn)在最久的時間戳。這樣的方法可以用數(shù)組,鏈表來實現(xiàn),時間復(fù)雜度一般是O(n)。

或者可以不用表示時間戳,我們僅根據(jù)數(shù)據(jù)的使用時間對數(shù)據(jù)進行排序,最近使用的排在前面,很久沒有使用的排在后面。刪除數(shù)據(jù)從后面開始刪除。在查找數(shù)據(jù)的時候可以使用HashMap。這樣,時間復(fù)雜度就可以簡化成O(1)。

說了這么多,我們把細節(jié)總結(jié)一下,說一下算法的具體實現(xiàn)。

1.首先我們應(yīng)該維護一個雙向鏈表,在這個鏈表中,我們應(yīng)該使鏈表中的數(shù)據(jù)這樣排列:越近使用的數(shù)據(jù)越排在前面,越久沒有使用的數(shù)據(jù)排在后面。此外,鏈表中的所有數(shù)據(jù)緩存在一個HashMap中。
2.我們插入一個數(shù)據(jù)的時候,首先查找HashMap中是否有這個數(shù)據(jù)?
2.1 如果沒有這個數(shù)據(jù),我們要判斷現(xiàn)在HashMap中是否還有剩余空間?如果沒有剩余空間,需要將雙向鏈表中最后一個數(shù)據(jù)刪除(在HashMap中也要刪除這個數(shù)據(jù),注意刪除的時候空指針等異常情況)。然后將要插入的數(shù)據(jù)放到雙向鏈表的頭部。
2.2 如果有這個數(shù)據(jù),我們首先應(yīng)該將這個數(shù)據(jù)與它的前驅(qū)和后驅(qū)斷開(注意可能造成空指針異常的情況),然后將這個數(shù)據(jù)插入到頭結(jié)點。
3. 當(dāng)我們需要獲取一個數(shù)據(jù)的時候,查找HashMap中是否有這個數(shù)據(jù)?
3.1 如果有這個數(shù)據(jù), 我們首先應(yīng)該將這個數(shù)據(jù)與它的前驅(qū)和后驅(qū)斷開(注意可能造成空指針異常的情況),然后將這個數(shù)據(jù)插入到頭結(jié)點。
3.2 如果沒有這個數(shù)據(jù),返回-1。

代碼

class LRUCache {
    /**
    * 需要實現(xiàn)以下
    * 1.維護一個雙向鏈表,還有他的頭結(jié)點和尾結(jié)點
    * 2.插入數(shù)據(jù)時,首先判斷cache中是否有該結(jié)點?如果沒有,檢查緩存是否還有空間?如果沒有空間,清除雙線鏈表的尾部結(jié)點。將該結(jié)點插入到雙向鏈表的頭部
    * 3.根據(jù)key獲得數(shù)據(jù)的時候,查看cache中是否有key對應(yīng)的結(jié)點?如果有,將該結(jié)點插入到頭結(jié)點前面,返回數(shù)據(jù)::如果沒有,返回-1。
    */
    private int capacity;
    private LinkNode first;
    private LinkNode last;
    private HashMap<Integer,LinkNode> cache;
    public LRUCache(int capacity) {
        this.capacity=capacity;
        cache = new HashMap<Integer,LinkNode>(capacity);
    }
    
    public int get(int key) {
        LinkNode res=cache.get(key);
        if(res==null){
            return -1;
        }
        moveNodeToFirst(res);
        return res.val;
    }
    
    public void put(int key, int value) {
        LinkNode temp=cache.get(key);
        if(temp==null){
            if(cache.size()>=capacity){
                removeLastNode();
            }
            temp=new LinkNode();
            temp.key=key;
        }
        temp.val=value;
        moveNodeToFirst(temp);
        cache.put(key,temp);
    }
    
    private void removeLastNode(){
        LinkNode temp=last;
        last=last.pre;
        if(last!=null){
            last.next=null;
        }else{
            first=last=null;
        }
        cache.remove(temp.key);
    }
    
    private void moveNodeToFirst(LinkNode node){
        if(first==node) return;
        if(node.pre!=null){
            node.pre.next=node.next;
        }
        if(node.next!=null){
            node.next.pre=node.pre;
        }
        if(node==last){
            last=last.pre;
        }
        if(last==null){
            last=first=node;
            return;
        }
        node.next=first;
        first.pre=node;
        node.pre=null;
        first=node;
    }
}

class LinkNode{
    LinkNode pre;
    LinkNode next;
    int key;
    int val;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. LRU 1.1.原理 LRU(Leastrecentlyused,最近最少使用)算法根據(jù)數(shù)據(jù)的歷史訪問記錄來...
    安易學(xué)車閱讀 2,619評論 0 23
  • 之前面試被問到了LRU Cache,之前沒接觸,現(xiàn)在學(xué)習(xí)補充一下。 什么是Cache Cache概念 Cache,...
    stoneyang94閱讀 8,692評論 1 10
  • ACache介紹: ACache類似于SharedPreferences,但是比SharedPreferences...
    遙遙的遠方閱讀 10,334評論 3 13
  • 1 早上7點40,喊妞起床,不起,5分鐘再喊,還不起,再喊,委屈的哭起來,剛開始是小聲抽泣,一分鐘后干脆大聲哭起來...
    a天下無雙a閱讀 146評論 0 1
  • 第一天來到這里 來欣賞 來借鑒 …… …… 或者自己心中還有一絲不甘 丟掉了的 遺忘了的自由 試圖從這里找回
    幺寶寶幺閱讀 208評論 1 0

友情鏈接更多精彩內(nèi)容