Leetcode 146. LRU 快取機制
阿新 • • 發佈:2021-09-07
前言
快取是一種提高資料讀取效能的技術,在計算機中cpu和主記憶體之間讀取資料存在差異,CPU和主記憶體之間有CPU快取,而且在記憶體和硬碟有記憶體快取。當主存容量遠大於CPU快取,或磁碟容量遠大於主存時,哪些資料應該被應該被清理,哪些資料應該被保留,這就需要快取淘汰策略來決定。常見的策略有三種:先進先出策略FIFO(First In,First Out)、最少使用策略LFU(Least Frequently Used)、最近最少使用策略LRU(Least Recently Used)。
LRU描述
設計和實現一個 LRU (最近最少使用) 快取機制 。
實現 LRUCache 類:
- LRUCache(int capacity) 以正整數作為容量capacity 初始化 LRU 快取
- int get(int key) 如果關鍵字 key 存在於快取中,則返回關鍵字的值,否則返回 -1 。
- void put(int key, int value)如果關鍵字已經存在,則變更其資料值;如果關鍵字不存在,則插入該組「關鍵字-值」。當快取容量達到上限時,它應該在寫入新資料之前刪除最久未使用的資料值,從而為新的資料值留出空間。
解題思路 雜湊表 + 雙向連結串列
- 針對LRU的特點,選擇使用雙鏈表實現。
- 使用 gut 方法獲取資料,如果有資料,把返回資料,並且把資料放在連結串列頭部。
- 使用 put 方法存放資料,如果資料存在,直接覆蓋新值;如果資料不存在,新增新值。新值都放在連結串列頭部。此外,還需要判斷快取有沒有超出容量 capacity,如果有超出,刪除連結串列的尾結點。
- 因為是單鏈表,每次獲取資料,或者刪除資料,都需要遍歷一遍連結串列,時間複雜度是O(n),這裡使用hash來記錄每個資料的位置,將資料訪問的時間複雜度降到O(1)。
class LRUCache { class DLinkedNode{ int key; int value; DLinkedNode prev; DLinkedNode next; public DLinkedNode() {} public DLinkedNode(int key, int value) { this.key = key; this.value = value; } } private int size; private int capacity; private DLinkedNode head; private DLinkedNode tail; private Map<Integer,DLinkedNode> cache = new HashMap<>(); public LRUCache(int capacity) { this.size = 0; this.capacity = capacity; head = new DLinkedNode(); tail = new DLinkedNode(); head.next = tail; tail.prev = head; } public int get(int key) { DLinkedNode node = cache.get(key); if (node == null) { return -1; } //找到並移動到首位 moveToHead(node); return node.value; } public void put(int key, int value) { DLinkedNode node = cache.get(key); if (node == null) { //不存在就建立一個新的節點 DLinkedNode newNode = new DLinkedNode(key,value); cache.put(key,newNode); addToHead(newNode); size++; if (size > capacity) { //超出容量,移除最後節點 DLinkedNode tail = removeTail(); cache.remove(tail.key); size--; } } else { //key存在,覆蓋value,並移到頭部 if (node.value != value) { node.value = value; } moveToHead(node); } } private DLinkedNode removeTail() { DLinkedNode node = tail.prev; removeNode(node); return node; } private DLinkedNode removeNode(DLinkedNode node) { node.next.prev = node.prev; node.prev.next = node.next; return node; } private void moveToHead(DLinkedNode node) { removeNode(node); addToHead(node); } private void addToHead(DLinkedNode node) { node.prev = head; node.next = head.next; head.next.prev = node; head.next = node; } }