摘要:因為是所有兩個操作的時間復雜度都必須是。因為使用線性的數據結構,并且表示操作的先后順序,這樣的結構就是鏈表。我們發現,無論是還是都有兩個簡單操作組成,從鏈表中移除,放到鏈表頭部。如果從尾部移除,將不會指向任何點。
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(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.
這題需要我們設計一個cache, 有get和set兩個操作。因為是cache, 所有兩個操作的時間復雜度都必須是O(1)。
get(key) -- O(1) 很明顯,我們需要用一個hashmap來實現O(1)的操作。
set(key, value) -- O(1) 這里有兩種情況,key沒出現過,就直接加在head。這里出現一個關鍵詞head。
因為使用線性的數據結構,并且表示操作的先后順序,這樣的結構就是鏈表。是單鏈表還是雙鏈表?下面我們模擬一下:
capacity = 3
set(1, 100)
set(2, 200)
set(3, 300)
get(2)
如果是單鏈表,簡單表示如下:
1 -> 2 -> 3 -> null
我們可以得到2并放在頭部。但是這里用的單鏈表,我們無法知道2的前面是什么,2前面的所有點都會脫離整體。所以需要一個雙鏈表。
1 <=> 3 <=> 2 <=> null
我們繼續操作,set(4, 400),發現已經達到LRU的容量,需要移除,這時候發現我們需要一個尾部來告訴我們需要移除哪個點。
我們發現,無論是get(key)還是set(key, value)都有兩個簡單操作組成,從鏈表中移除,放到鏈表頭部。
可以定義兩個helper function: remove(node), setHead(node)。
代碼如下,帶注釋:
public class LRUCache { class Node{ int key; int value; Node pre; // point to tail direction Node next; // point to head direction public Node(int key, int value){ this.key = key; this.value = value; } } int capacity; Mapmap = new HashMap<>(); Node tail = null; Node head = null; public LRUCache(int capacity) { this.capacity = capacity; } public int get(int key) { if(map.containsKey(key)){ // remove from LRU and put it to head of LRU Node n = map.get(key); remove(n); setHead(n); return n.value; } return -1; } public void set(int key, int value) { if(map.containsKey(key)){ // change node value, remove from LRU and put it to head of LRU Node old = map.get(key); old.value = value; remove(old); setHead(old); } else { Node newNode = new Node(key, value); if(capacity == map.size()){ // remove the tail map.remove(tail.key); remove(tail); } setHead(newNode); // set newNode to head map.put(key, newNode); } } public void remove(Node n){ if(n.pre != null) { // change pre node connection n.pre.next = n.next; } else { // check if it is the tail tail = n.next; } if(n.next != null) { // change next node connection n.next.pre = n.pre; } else { // check if it is the head head = n.pre; } } public void setHead(Node n){ n.pre = head; n.next = null; if(head != null) { // check head exist or Not ? head.next = n; } head = n; if(tail == null){ // empty LRU, intitailize tail node tail = head; } } }
使用dummyEnd 和dummyHead可以簡化代碼。
public class LRUCache { int capacity; Mapmap; Node dummyEnd; Node dummyHead; int count; public LRUCache(int capacity) { this.capacity = capacity; this.count = 0; map = new HashMap (); dummyEnd = new Node(0,0); dummyHead = new Node(0,0); dummyEnd.next = dummyHead; dummyHead.pre = dummyEnd; } public int get(int key) { Node node = map.get(key); if(node == null) { return -1; } else { remove(node); putToHead(node); return node.val; } } public void put(int key, int value) { Node oldNode = map.get(key); if(oldNode == null) { ++count; Node newNode = new Node(key, value); map.put(key, newNode); putToHead(newNode); if(count > capacity){ // 從LRU移除 // 第一次在這里debug了好久,要先取出nextNode, 不然map里remove的就是錯誤的點,即dummy.next.next。 Node nextNode = dummyEnd.next; remove(nextNode); // 從map移除 map.remove(nextNode.key); --count; } } else { // 改變值,先移除,再放入頭部 oldNode.val = value; remove(oldNode); putToHead(oldNode); } } public void putToHead(Node node){ // 加到頭和前一個點的中間 Node preNode = dummyHead.pre; preNode.next = node; node.pre = preNode; dummyHead.pre = node; node.next = dummyHead; } public void remove(Node node){ // 移除。 node.next.pre = node.pre; node.pre.next = node.next; // node 如果從尾部移除,將不會指向任何點。 node.pre = null; node.next = null; } class Node{ int key, val; Node pre, next; public Node(int key, int val){ this.key = key; this.val = val; } } } /** * Your LRUCache object will be instantiated and called as such: * LRUCache obj = new LRUCache(capacity); * int param_1 = obj.get(key); * obj.put(key,value); */
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/66402.html
摘要:酷庫,每天兩分鐘,了解一個流行庫。而直接將數據保存在程序變量中,最經濟快捷。但是這樣就會帶來一些其他問題,比如緩存更新緩存過期等。用于在內存中管理緩存數據,并且支持算法。可以讓程序不依賴任何外部數據庫實現緩存管理。 NPM酷庫,每天兩分鐘,了解一個流行NPM庫。 為了優化程序性能,我們常常需要獎數據緩存起來,根據實際情況,我們可以將數據存儲到磁盤、數據庫、redis等。 但是有時候要緩...
摘要:酷庫,每天兩分鐘,了解一個流行庫。而直接將數據保存在程序變量中,最經濟快捷。但是這樣就會帶來一些其他問題,比如緩存更新緩存過期等。用于在內存中管理緩存數據,并且支持算法??梢宰尦绦虿灰蕾嚾魏瓮獠繑祿鞂崿F緩存管理。 NPM酷庫,每天兩分鐘,了解一個流行NPM庫。 為了優化程序性能,我們常常需要獎數據緩存起來,根據實際情況,我們可以將數據存儲到磁盤、數據庫、redis等。 但是有時候要緩...
摘要:在閱讀的源代碼的時候,發現其中的類正是一個線程安全的實現,代碼非常優雅。至此一個線程安全的類就已經全部實現,在中使用的緩存是,其實就是聚合多個實例,真正的邏輯都在類中。 緩存是計算機的每一個層次中都是一個非常重要的概念,緩存的存在可以大大提高軟件的運行速度。Least Recently Used(lru) cache 即最近最久未使用的緩存,多見與頁面置換算法,lru 緩存算法在緩存的...
摘要:劍指緩存實現聲明文章均為本人技術筆記,轉載請注明出處解題思路緩存兩種功能獲取的對應,不存在返回版本版本設置緩存已滿,刪除最近最久未被使用的節點,添加新節點進緩存緩存未滿,節點存在,修改節點不存在,添加新節點進緩存解題思路由于緩存插入和刪除 劍指offer/LeetCode146/LintCode134_LRU緩存實現 聲明 文章均為本人技術筆記,轉載請注明出處[1] https://s...
摘要:簡介概述緩存資源通常比較昂貴通常數據量較大時會竟可能從較少的緩存滿足盡可能多訪問這里有一種假設通常最近被訪問的數據那么它就有可能會被后續繼續訪問基于這種假設將所有的數據按訪問時間進行排序并按驅逐出舊數據那么存在緩存的數據就為熱點數據這樣既節 1. LRU簡介 1.1 概述 緩存資源通常比較昂貴,通常數據量較大時,會竟可能從較少的緩存滿足盡可能多訪問,這里有一種假設,通常最近被訪問的數據...
閱讀 931·2023-04-26 01:34
閱讀 3365·2023-04-25 20:58
閱讀 3296·2021-11-08 13:22
閱讀 2120·2019-08-30 14:17
閱讀 2527·2019-08-29 15:27
閱讀 2680·2019-08-29 12:45
閱讀 3006·2019-08-29 12:26
閱讀 2818·2019-08-28 17:51