LRU, 2022-10-17

(2022.10.17 Mon)
LRU, Least Recently Used是一種頁(yè)面置換算法(page replacement algorithm),其應(yīng)用包括Python內(nèi)存管理和智能手機(jī)"最近任務(wù)"等場(chǎng)景霎终。LRU將剛剛使用的任務(wù)提前,選擇最久未使用的任務(wù)予以淘汰异旧。

比如手機(jī)的最近任務(wù)瀏覽功能,你可以在這里看到按時(shí)間順序排列的最近使用的功能提佣,每次當(dāng)點(diǎn)擊某個(gè)app吮蛹,其對(duì)應(yīng)的圖標(biāo)就會(huì)出現(xiàn)在最近任務(wù)列表的第一位,其他已經(jīng)保存的任務(wù)依次向后移動(dòng)拌屏。


LRU_case.jpeg

LRU的實(shí)現(xiàn)

在了解了LRU的基本特征之后潮针,這部分我們實(shí)現(xiàn)LRU。根據(jù)其特點(diǎn)描述倚喂,每個(gè)app/任務(wù)會(huì)在使用之后放置于整個(gè)LRU隊(duì)列的最前端每篷,其他任務(wù)向后移動(dòng)。該部分可考慮使用隊(duì)列(array)或鏈表(linked list)實(shí)現(xiàn)端圈。

使用array實(shí)現(xiàn)焦读,優(yōu)勢(shì)在于占用空間小且連續(xù)(考慮到鏈表的每個(gè)元素需要保存指向其他元素的指針),訪問元素只需要通過index即可實(shí)現(xiàn)枫笛,復(fù)雜度為常數(shù)時(shí)間O(1)吨灭;劣勢(shì)在于當(dāng)某任務(wù)被前置時(shí),排名其后的所有元素都要向后移動(dòng)刑巧,復(fù)雜度為O(n)

使用linked list實(shí)現(xiàn)无畔,優(yōu)勢(shì)在于其中元素的位置調(diào)整靈活啊楚,只需要調(diào)整前后指針,復(fù)雜度為O(1)浑彰;劣勢(shì)在于占用空間相比array更多恭理,比如每個(gè)元素需要包含指向其他元素的指針,訪問元素需要從頭開始遍歷郭变,最壞情況達(dá)到了O(n)颜价。

然而在Linked list的實(shí)現(xiàn)中涯保,可通過hash table的方式,將訪問元素的復(fù)雜度從O(n)降為O(1)周伦。

實(shí)現(xiàn):Doubly Linked List

在雙向鏈表中夕春,每個(gè)元素保留向前和向后兩個(gè)指針,為了hash table使用专挪,也保留了key和element/value兩個(gè)字段的信息及志。

該P(yáng)ython實(shí)現(xiàn)中提供了三個(gè)類,Node寨腔,doublyLinkedListLRU速侈,其中前兩個(gè)是LRU類實(shí)現(xiàn)的基礎(chǔ)。這里的hash table實(shí)現(xiàn)采用了Python的dict迫卢。

  • Node類:代表了每個(gè)任務(wù)/app倚搬,包含4個(gè)字段,key乾蛤,value潭枣,previous pointernext pointer
  • doublyLinkedList類:雙向鏈表幻捏,可實(shí)現(xiàn)刪除點(diǎn)盆犁、尾部彈出、從頭加入節(jié)點(diǎn)篡九。
  • LRU類:包含兩個(gè)方法谐岁,setget,前者為加入新的任務(wù)/app榛臼,在加入該新的對(duì)象時(shí)伊佃,自動(dòng)將其放在鏈表的頭部,后者為訪問特定任務(wù)/app沛善,訪問成功后將該對(duì)象置于鏈表的頭部航揉。
class Node:
    """
    Node object, including 4 fields, i.e., key, value, previous&next pointer
    """
    def __init__(self, key, value, prev=None, nex=None):
        self._key = key
        self._value = value
        self._prev = prev
        self._next = nex

class doublyLinkedList(object):
    def __init__(self):
        """
        head and tail work as placeholder
        """
        self._head = Node(None, None)
        self._tail = Node(None, None)
        self._len = 0

    def append_front(self, node):
        """
        add a node to the front
        """
        if self._len <= 0:
            logging.info(f"""into append_front method: {node}""")
            node._prev = self._head
            node._next = self._tail
            self._head._next = node
            self._tail._prev = node
            self._len += 1
            return
        old_head = self._head._next
        self._head._next = node
        node._prev = self._head
        node._next = old_head
        if old_head:
            old_head._prev = node
        self._len += 1

    def remove(self, node):
        """
        remove a node, update the pointers of prev&next nodes
        """
        logging.info(f"""into remove method of DLL: {node}""")
        logging.info(f"""prev pointer: {node._prev}""")
        prev = node._prev
        logging.info(f"""next pointer: {node._next}""")
        nex = node._next
        if node == self._head._next:
            self._head._next = nex
        if node == self._tail._prev:
            self._tail._prev = prev
        if prev:
            prev._next = nex
        if nex:
            nex._prev = prev
    
        node._prev = None
        node._next = None
        self._len -= 1
        return node

    def pop_tail(self):
        """
        remove last node in the list
        """
        tail = self._tail._prev
        if not tail:
            return tail
        prev_tail = tail._prev
        self._tail = prev_tail
        self._len -= 1
        if prev_tail:
            prev_tail._next = self._tail
        return tail

class LRU():
    """
    LRU is based on doubly linked list, setup capacity though
    """
    def __init__(self, capacity=5):
        self._capacity = capacity
        self._keys = {}
        self._list = doublyLinkedList()

    def set(self, key, value):
        """
        add a new node/element
        """
        if key in self._keys:
            logging.info(f"""key exists in the hash table: {key}""")
            node = self._keys[key]
            node._value = value
            logging.info(f"""about to conduct remove and append_front operation: {key}""")
            logging.info(f"""remove first: {key}""")
            r_node = self._list.remove(node)
            logging.info(f"""append front follows: {key}""")
            self._list.append_front(r_node)
            print(self._list._len, self._list)
            return f"""{key} updated: {value}"""
        if self._list._len >= self._capacity:
            p_node = self._list.pop_tail()
            if p_node:
                del self._keys[p_node._key]
                del p_node
        logging.info(f"""about to add new key to the hash map: {key}""")
        node = Node(key, value)
        self._keys[key] = node
        logging.info(f"""conduct append_front operation: {key}""")
        self._list.append_front(node)
        print("SET - key: %s, value: %s" % (key, value))
        print("length: ", self._list._len, ", head:", self._list._head._next._value)

    def get(self, key):
        """
        access to an existing node in the list
        """
        if key not in self._keys:
            return None
        logging.info(f"""get method: key {key} in the list""")
        node = self._keys[key]
        logging.info(f"""get method: conduct remove and append_front operations""")
        r_node = self._list.remove(node)
        self._list.append_front(r_node)
        print("GET - key: %s, value: %s" % (key, node._value))
        return node._value

運(yùn)行 查看過程

if __name__ == '__main__':
    format = "%(asctime)s: %(message)s"
    logging.basicConfig(format=format, level=logging.INFO)
    lru = LRU(3)
    lru.set('name', 'jeff')
    lru.set('gender', 'male')
    lru.set('age', 39)
    lru.set('location', 'Hong Kong')
    lru.get('age')
    # lru.set
    print('lru keys: ', lru._keys)
    print('lru list: ', lru._list)

實(shí)現(xiàn):Redis

Redis提供了LRU的實(shí)現(xiàn)方案。
placeholder

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末金刁,一起剝皮案震驚了整個(gè)濱河市帅涂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尤蛮,老刑警劉巖媳友,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異产捞,居然都是意外死亡醇锚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門坯临,熙熙樓的掌柜王于貴愁眉苦臉地迎上來焊唬,“玉大人恋昼,你說我怎么就攤上這事「洗伲” “怎么了液肌?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)芳杏。 經(jīng)常有香客問我矩屁,道長(zhǎng),這世上最難降的妖魔是什么爵赵? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任吝秕,我火速辦了婚禮,結(jié)果婚禮上空幻,老公的妹妹穿的比我還像新娘烁峭。我一直安慰自己,他們只是感情好秕铛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布约郁。 她就那樣靜靜地躺著,像睡著了一般但两。 火紅的嫁衣襯著肌膚如雪鬓梅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天谨湘,我揣著相機(jī)與錄音绽快,去河邊找鬼。 笑死紧阔,一個(gè)胖子當(dāng)著我的面吹牛坊罢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播擅耽,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼活孩,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了乖仇?” 一聲冷哼從身側(cè)響起憾儒,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎这敬,沒想到半個(gè)月后航夺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崔涂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了始衅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冷蚂。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缭保,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蝙茶,到底是詐尸還是另有隱情艺骂,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布隆夯,位于F島的核電站钳恕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蹄衷。R本人自食惡果不足惜忧额,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望愧口。 院中可真熱鬧睦番,春花似錦、人聲如沸耍属。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)厚骗。三九已至示启,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間领舰,已是汗流浹背夫嗓。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留提揍,地道東北人啤月。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像劳跃,于是被迫代替她去往敵國(guó)和親谎仲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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