146. LRU Cache (Medium)

Description:

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

The cache is initialized with a positive capacity.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.put(4, 4); // evicts key 1
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4


Solutions:

Brute force:

from collections import deque
class LRUCache:

    def __init__(self, capacity: int):
        self.q = deque()
        self.dic = {}
        self.cap = capacity
        self.count = 0

    def get(self, key: int) -> int:
        if key in self.dic:
            self.q.remove(key)
            self.q.append(key)
            return self.dic[key]
        return -1

    def put(self, key: int, value: int) -> None:
        self.count += 1
        if key not in self.dic:
            self.q.append(key)
        else:
            self.q.remove(key)
            self.q.append(key)
        self.dic[key] = value
        if len(self.q) > self.cap:
            self.dic.pop(self.q.popleft())

Hashtable + double linked list: 野路子

class Node:
    def __init__(self,prev,key,value,next_,):
        self.prev = prev
        self.key = key
        self.val = value
        self.next = next_
        
class LRUCache:
    def __init__(self, capacity: int):
        self.cap = capacity+1
        self.zero = Node(None,None,None,None)
        self.left = self.zero
        self.right = self.zero
        self.dic = {None:self.zero}
        self.count = 1

    def get(self, key: int) -> int:
        if key in self.dic:
            if self.dic[key] != self.right:
                prev = self.dic[key].prev
                next_ = self.dic[key].next
                prev.next = next_
                next_.prev = prev
                if self.dic[key] == self.left:
                    self.left = self.zero.next
                self.dic[key].prev = self.right
                self.right.next = self.dic[key]
                self.right = self.dic[key]
            return self.dic[key].val
        return -1

    def put(self, key: int, value: int) -> None:
        if key not in self.dic:
            self.count += 1
            self.dic[key] = Node(self.right,key,value,None)
            if self.count == 2:
                self.left = self.dic[key]
            self.right.next = self.dic[key]
            self.right = self.dic[key]
            if self.count > self.cap:
                self.dic.pop(self.left.key)
                self.left = self.left.next
                self.zero.next = self.left
                self.left.prev = self.zero
                self.count -= 1
        elif self.dic[key] == self.right:
            self.dic[key].val = value
        else:
            prev = self.dic[key].prev
            next_ = self.dic[key].next
            prev.next = next_
            next_.prev = prev
            if self.dic[key] == self.left:
                self.left = self.zero.next
            self.dic[key].val = value
            self.dic[key].prev = self.right
            self.right.next = self.dic[key]
            self.right = self.dic[key]

Runtime: 244 ms, faster than 43.29% of Python3 online submissions for LRU Cache.
Memory Usage: 23.2 MB, less than 5.07% of Python3 online submissions for LRU Cache.

sample 192 ms submission: Cheating solution

NOTE: 重點在于使用del self.d[next(iter(self.d))]刪除頭部元素『氐欤看來是Python dictionary內(nèi)置的功能了。

class LRUCache:
    def __init__(self, capacity: int):
        self.cap = capacity
        self.d = {}

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

        # move the key to the 'end' of the dictionary
        del self.d[key]
        self.d[key] = value

        return value

    def put(self, key: int, value: int) -> None:
        # place the key at the 'end' of the dictionary
        if key in self.d:
            del self.d[key]
        self.d[key] = value

        # if over capacity, delete the 'front' of the dictionary
        if len(self.d) > self.cap:
            del self.d[next(iter(self.d))]

Using OrderedDict

from collections import OrderedDict
class LRUCache:

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

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

    def put(self, key: int, value: int) -> None:
        if key not in self.dic:
            self.dic[key] = value
            if len(self.dic) > self.cap:
                self.dic.popitem(last=False)
        else:
            self.dic[key] = value
            self.dic.move_to_end(key)

Runtime: 192 ms, faster than 99.80% of Python3 online submissions for LRU Cache.
Memory Usage: 23 MB, less than 5.07% of Python3 online submissions for LRU Cache.

https://docs.python.org/3.7/library/collections.html#collections.OrderedDict

CPP Solution: combine linked_list and hashmap

Inspired by https://zxi.mytechroad.com/blog/hashtable/leetcode-146-lru-cache/

class LRUCache {
public:
    LRUCache(int capacity_) {
        capacity = capacity_;
    }
    
    int get(int key) {
        const auto it = dic.find(key);
        if (it != dic.cend()){
            ls.splice(ls.begin(),ls,it->second);
            return it->second->second;
        }
        return -1;
    }
    
    void put(int key, int value) {
        const auto it = dic.find(key);
        if (it != dic.cend()){
            ls.splice(ls.begin(),ls,it->second);
            it->second->second = value;
        }
        else{
            ls.emplace_front(key,value);
            dic[key] = ls.begin();
            if (ls.size() > capacity){
                dic.erase(ls.back().first);
                ls.pop_back();
            }
        }
    }
private:
    int capacity;
    list<pair<int,int>> ls;
    unordered_map<int,list<pair<int,int>>::iterator> dic;
};

Runtime: 88 ms, faster than 99.62% of C++ online submissions for LRU Cache.
Memory Usage: 38 MB, less than 78.53% of C++ online submissions for LRU Cache.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子禾乘,更是在濱河造成了極大的恐慌瓤狐,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件而钞,死亡現(xiàn)場離奇詭異,居然都是意外死亡拘荡,警方通過查閱死者的電腦和手機臼节,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來珊皿,“玉大人网缝,你說我怎么就攤上這事◇ǎ” “怎么了粉臊?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長驶兜。 經(jīng)常有香客問我扼仲,道長远寸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任屠凶,我火速辦了婚禮驰后,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘矗愧。我一直安慰自己灶芝,他們只是感情好,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布唉韭。 她就那樣靜靜地躺著夜涕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪属愤。 梳的紋絲不亂的頭發(fā)上女器,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機與錄音春塌,去河邊找鬼晓避。 笑死簇捍,一個胖子當著我的面吹牛只壳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播暑塑,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼吼句,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了事格?” 一聲冷哼從身側(cè)響起惕艳,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驹愚,沒想到半個月后远搪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡逢捺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年谁鳍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劫瞳。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡倘潜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出志于,到底是詐尸還是另有隱情涮因,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布伺绽,位于F島的核電站养泡,受9級特大地震影響嗜湃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜澜掩,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一净蚤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧输硝,春花似錦今瀑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至郎逃,卻和暖如春哥童,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背褒翰。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工贮懈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人优训。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓朵你,卻偏偏與公主長得像,于是被迫代替她去往敵國和親揣非。 傳聞我的和親對象是個殘疾皇子抡医,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,345評論 0 10
  • **2014真題Directions:Read the following text. Choose the be...
    又是夜半驚坐起閱讀 9,572評論 0 23
  • pyspark.sql模塊 模塊上下文 Spark SQL和DataFrames的重要類: pyspark.sql...
    mpro閱讀 9,464評論 0 13
  • 今天是營會的第一天,很意外早敬,很驚喜忌傻,很收獲。羅俊義老師讓我看到了一個基督徒的多種可能性搞监,而不僅僅是為了傳福音水孩,更不...
    切勿漫不經(jīng)心閱讀 53評論 0 0
  • 距離外公去世已有兩年了,他走得太過匆忙琐驴,只有外婆見了他最后一面俘种,我們這些這些晚輩一個都不在,也為著沒見到外公最后一...
    菩提樹下一粒沙閱讀 203評論 0 2