內存淘汰機制——LRU與LFU


內存淘汰機制之LRU與LFU

  • LRU(Least Recently Used):淘汰 近期最不會訪問的數據
  • LFU(Least Frequently Used):淘汰 最不經常使用(訪問次數少)

所謂淘汰就是將內存中指定部分的數據移除,釋放空間提供給新來的數據。

LRU

LeetCode入口👉👉👉No.146

image-20200505211459196

  • 存數據,將數據插入鏈表頭部;如果內存滿了,需要先將鏈表尾部數據刪除,再插入

  • 取數據,每次將取到的數據重新放到鏈表頭部

LRU一般使用哈希鏈表(哈希表+雙向鏈表)實現,可以在 \(O(1)\) 復雜度內實現插入、刪除。

OrderedDict 的 popitem 方法默認刪除並返回的是字典里的最后一個元素;popitem(last=False) 刪除並返回第一個被添加進去的元素

coding:

#--python
#使用python自帶哈希鏈表(有序字典OrderedDict)實現
from collections import OrderedDict
class LRUCache(OrderedDict):

    def __init__(self, capacity: int):
        self.capacity = capacity

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

    def put(self, key: int, value: int) -> None:
        if key in self:
            self.move_to_end(key)
        self[key] = value	#更新value
        if len(self) > self.capacity:
            self.popitem(last=False)

自己實現哈希鏈表

#--python
#定義雙向鏈表節點
class DLinkNode():
    def __init__(self,key=0,value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache():
    def __init__(self,capacity):
        self.cache = {}
        self.size = 0
        self.capacity = capacity
        #定義偽頭部 偽尾部節點
        self.head,self.tail = DLinkNode(),DLinkNode()
        self.head.next = self.tail
        self.tail.prev = self.head
    
    #模擬OrderedDict 定義 添加 刪除 移動頭部方法
    def _add_node(self,node):
        node.prev = self.head
        node.next = self.head.next

        self.head.next.prev = node
        self.head.next = node
    
    def _remove_node(self,node):
        node.next.prev = node.prev
        node.prev.next = node.next
    
    def _move_to_head(self,node):
        self._remove_node(node)
        self._add_node(node)

    def _pop_tail(self):
        node = self.tail.prev
        self._remove_node(node)
        return node

    def get(self,key):
        if key not in self.cache:
            return -1
        node = self.cache[key]
        self._move_to_head(node)
        return node.value
    
    def put(self,key,value):
        #如果key不存在,創建node 添加
        if key not in self.cache:
            node = DLinkNode(key,value)
            self.cache[key] = node
            self._add_node(node)
            self.size += 1
            #如果滿了,刪除雙向鏈表節點 和 字典對應鍵
            if self.size > self.capacity:
                node = self._pop_tail()
                self.cache.pop(node.key)
                self.size -= 1
        else:
            node = self.cache[key]
            node.value = value
            self._move_to_head(node)

LFU

LeetCode入口👉👉👉No.460

image-20200505211548831

  • 維護一個訪問頻次的數據結構,取數據,訪問頻次加一,根據訪問次數排序
  • 存數據,當緩存滿時,淘汰點訪問次數最小的

使用雙哈希表 keyMap 和 freqMap

coding:

#--python
#雙哈希表 
class Node:
    def __init__(self, key, val, pre=None, nex=None, freq=0):
        self.pre = pre
        self.nex = nex
        self.freq = freq
        self.val = val
        self.key = key
        
    def insert(self, nex):
        nex.pre = self
        nex.nex = self.nex
        self.nex.pre = nex
        self.nex = nex
    
def create_linked_list():
    head = Node(0, 0)
    tail = Node(0, 0)
    head.nex = tail
    tail.pre = head
    return (head, tail)

class LFUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.size = 0
        self.minFreq = 0
        self.freqMap = collections.defaultdict(create_linked_list)
        self.keyMap = {}

    def delete(self, node):
        if node.pre:
            node.pre.nex = node.nex
            node.nex.pre = node.pre
            if node.pre is self.freqMap[node.freq][0] and node.nex is self.freqMap[node.freq][-1]:
                self.freqMap.pop(node.freq)
        return node.key
        
    def increase(self, node):
        node.freq += 1
        self.delete(node)
        self.freqMap[node.freq][-1].pre.insert(node)
        if node.freq == 1:
            self.minFreq = 1
        elif self.minFreq == node.freq - 1:
            head, tail = self.freqMap[node.freq - 1]
            if head.nex is tail:
                self.minFreq = node.freq

    def get(self, key: int) -> int:
        if key in self.keyMap:
            self.increase(self.keyMap[key])
            return self.keyMap[key].val
        return -1

    def put(self, key: int, value: int) -> None:
        if self.capacity != 0:
            if key in self.keyMap:
                node = self.keyMap[key]
                node.val = value
            else:
                node = Node(key, value)
                self.keyMap[key] = node
                self.size += 1
            if self.size > self.capacity:
                self.size -= 1
                deleted = self.delete(self.freqMap[self.minFreq][0].nex)
                self.keyMap.pop(deleted)
            self.increase(node)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM