1. 程式人生 > 實用技巧 >簡單實現LRU

簡單實現LRU

要求

設計和實現一個LRU(最近最少使用) 快取機制。它應該支援以下操作: 獲取資料 get和 寫入資料save ,這兩種操作的時間複雜度都為O(1)

get: 如果key存在於快取中,則獲取金鑰的值(總是正數),否則返回 -1。

save(key, value):如果key不存在,則寫入其資料值,如果存在,覆蓋其原來資料值。當快取容量達到上限時,它應該在寫入新資料之前刪除最近最少使用的資料值,從而為新的資料值留出空間。

Example:

LRUCache cache = new LRUCache(2);

cache.save(1, 1);
cache.save(2, 2);
cache.get(1);       // 返回  1
cache.save(3, 3);    // 該操作會使得金鑰 2 作廢
cache.get(2);       // 返回 -1 (未找到)
cache.save(4, 4);    // 該操作會使得金鑰 1 作廢
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

實現

要進行插入和讀取的複雜度都為O(1),可以使用雙端連結串列以及字典來實現。因此需要自定義一個雙端連結串列節點,以及雙端連結串列,與字典一起構成個LRU

每當插入新值/修改值/獲得值時,都需要將節點插入到連結串列頭。當連結串列超過自定義大小時,將尾端節點刪除。

下面是一個簡單實現(沒有進行異常處理):

# -*- coding: utf-8 -*-
# author: May
# Time: 2019-05-27 18:01
class Node(object):
    def __init__(self, key, value):
        self.key = key
        self.value = value
        self.pre = None
        self.nxt = None


class DoubleLinklist(object):
    def __init__(self):
        self.head = Node(0, 0)
        self.tail = Node(0, 0)
        self.head.nxt = self.tail
        self.tail.pre = self.head
        self.size = 0

        
class LRU(object):
    def __init__(self, size):
        self.link = DoubleLinklist()
        self.hash_map = {}
        self.size = size

    def add_to_head(self, node):
        f_node = self.link.head.nxt
        # 如果第一個節點是自己,不移動,否則會使自己指向自己
        if node != f_node:
            self.link.head.nxt, node.pre = node, self.link.head
            node.nxt, f_node.pre = f_node, node

    def remove_node(self, node):
        p_node, n_node = node.pre, node.nxt
        p_node.nxt, n_node.pre = n_node, p_node


    def save(self, key, value):
        # 節點存在,則直接更改值,放置於連結串列頭
        if key in self.hash_map:
            node = self.hash_map[key]
            node.value = value

            self.add_to_head(node)

        else:
            # 否則建立一個新節點,放置於連結串列頭
            node = Node(key, value)
            self.add_to_head(node)
            self.hash_map[key] = node
            self.link.size += 1

            # 當前連結串列大小大於指定大小時,才需要刪除最久未使用的節點
            if self.link.size > self.size:
                t_node = self.link.tail.pre
                self.remove_node(t_node)
                del self.hash_map[t_node.key]
                del t_node
                self.link.size -= 1
        return 'SUCCESS'

    def get(self, key):
        if key in self.hash_map:
            node = self.hash_map[key]
            self.add_to_head(node)
            return node.value
        else:
            return -1

    def delete(self, key):
        if key in self.hash_map:
            node = self.hash_map[key]
            self.remove_node(node)
            self.link.size -= 1
            del self.hash_map[key]
            del node
            return 'SUCCESS'
        else:
            return -1


cache = LRU(3)

print(cache.save(1, 1))
# SUCCESS
print(cache.save(2, 2))
# SUCCESS
print(cache.save(1, 3))
# SUCCESS
print(cache.get(1))
# 3
print(cache.save(3, 3))
# SUCCESS
print(cache.get(2))
# 2
print(cache.save(4, 4))
# SUCCESS
print(cache.get(1))
# -1
print(cache.get(3))
# 3
print(cache.get(4))
# 4
print(cache.delete(4))
# SUCCESS
print(cache.get(4))
# -1
print(cache.delete(1))
# -1
print(cache.delete(3))
# SUCCESS
print(cache.get(3))
# -1

References:

LRU cache