[LeetCode]146. LRU Cache 深入浅出讲解和代码示例

1、汇总概要

以下思路涵盖了哈希与双向链表的结合使用、缓存设计等知识点

2、题目

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(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.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

3、审题

设计一个简单版的最近使用缓存模型。缓存空间有容量限制,时间复杂度要求是O(1)。

其中“最近使用”是指最近被访问过(被cache.get调用过)。

4、解题思路

以上对cache的操作有:添加(put)、查找(get)、替换(put),因有容量限制,还需有删除,每次当容量满时,将最久未使用的节点删除。

为快速添加和删除,我们可以用双向链表来设计cache,链表中从头到尾的数据顺序依次是,(最近访问)->...(最旧访问):

1)添加节点:新节点插入到表头即可,时间复杂度O(1);

2)查找节点:每次节点被查询到时,将节点移动到链表头部,时间复杂度O(n)

3)  替换节点:查找到后替换(更新节点value),将节点移动到链表头部;

可见在查找节点时,因对链表需遍历,时间复杂度O(n),为达到O(1),可以考虑数据结构中加入哈希(hash)。

=>我们需要用两种数据结构来解题:双向链表、哈希表

示意图如下:


5、代码示例 - Java

[java]  view plain  copy
  1. import java.util.*;  
  2.   
  3. class Node{  
  4.     int key;  
  5.     int value;  
  6.     Node next;  
  7.     Node pre;  
  8.     public Node(int key,int value,Node pre, Node next){  
  9.         this.key = key;  
  10.         this.value = value;  
  11.         this.pre = pre;  
  12.         this.next = next;  
  13.     }  
  14. }  
  15.   
  16. public class LRUCache {  
  17.     int capacity;  
  18.     int count;//cache size  
  19.     Node head;  
  20.     Node tail;  
  21.     HashMap<Integer,Node>hm;  
  22.     public LRUCache(int capacity) { //only initial 2 Node is enough, head/tail  
  23.         this.capacity = capacity;  
  24.         this.count = 2;  
  25.         this.head = new Node(-1,-1,null,null);  
  26.         this.tail = new Node(-2,-2,this.head,null);  
  27.         this.head.next = this.tail;  
  28.         hm = new HashMap<Integer,Node>();  
  29.         hm.put(this.head.key, this.head);  
  30.         hm.put(this.tail.key, this.tail);  
  31.     }  
  32.       
  33.     public int get(int key) {  
  34.         int value = -1;  
  35.         if(hm.containsKey(key)){  
  36.             Node nd = hm.get(key);  
  37.             value = nd.value;  
  38.             detachNode(nd); //detach nd from current place  
  39.             insertToHead(nd); //insert nd into head  
  40.         }  
  41.         return value;  
  42.     }  
  43.       
  44.     public void put(int key, int value) {  
  45.         if(hm.containsKey(key)){ //update  
  46.             Node nd = hm.get(key);  
  47.             nd.value = value;  
  48.             //move to head  
  49.             detachNode(nd); //detach nd from current place  
  50.             insertToHead(nd); //insert nd into head  
  51.         }else//add  
  52.             Node newNd = new Node(key,value,null,this.head);  
  53.             this.head.pre = newNd; //insert into head  
  54.             this.head = newNd;  
  55.             hm.put(key, newNd); //add into hashMap  
  56.             this.count ++;  
  57.             if(this.count > capacity){ //need delete node  
  58.                 removeNode();  
  59.             }  
  60.         }  
  61.     }  
  62.     //common func  
  63.     public void insertToHead(Node nd){  
  64.         this.head.pre = nd;  
  65.         nd.next = this.head;  
  66.         nd.pre = null;  
  67.         this.head = nd;  
  68.     }  
  69.     public void detachNode(Node nd){  
  70.         nd.pre.next = nd.next;  
  71.         if(nd.next!=null){  
  72.             nd.next.pre = nd.pre;  
  73.         }else{  
  74.             this.tail = nd.pre;  
  75.         }  
  76.     }  
  77.     public void removeNode(){ //remove from tail  
  78.         int tailKey = this.tail.key;  
  79.         this.tail = this.tail.pre;  
  80.         this.tail.next = null;  
  81.         hm.remove(tailKey);  
  82.         this.count --;  
  83.     }  
  84.     public void printCache(){  
  85.         System.out.println("\nPRINT CACHE ------ ");  
  86.         System.out.println("count: "+count);  
  87.         System.out.println("From head:");  
  88.         Node p = this.head;  
  89.         while(p!=null){  
  90.             System.out.println("key: "+p.key+" value: "+p.value);  
  91.             p = p.next;  
  92.         }  
  93.         System.out.println("From tail:");  
  94.         p = this.tail;  
  95.         while(p!=null){  
  96.             System.out.println("key: "+p.key+" value: "+p.value);  
  97.             p = p.pre;  
  98.         }  
  99.           
  100.     }  
  101.       
  102.     public static void main(String[] args){  
  103.         LRUCache lc = new LRUCache(3);  
  104.         lc.printCache();  
  105.           
  106.         lc.put(11);  
  107.         lc.put(22);  
  108.         lc.put(33);  
  109.         lc.printCache();  
  110.           
  111.         lc.get(2);  
  112.         lc.printCache();  
  113.           
  114.         lc.put(44);  
  115.         lc.printCache();  
  116.           
  117.         lc.get(1);  
  118.         lc.printCache();  
  119.           
  120.         lc.put(333);  
  121.         lc.printCache();      
  122.     }  
  123. }  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值