lotus

贵有恒何必三更眠五更起 最无益只怕一日曝十日寒

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
  1846 随笔 :: 0 文章 :: 109 评论 :: 288万 阅读

1. 题目

 

https://leetcode.cn/problems/lru-cache/

 

2. 解法

解题思路是

使用一个双向链表和一个哈希表来实现一个LRU缓存机制。

双向链表用来存储最常使用的键值对,最近使用的元素放在链表的表头,链表中最后一个元素是使用频率最低的元素。

哈希表用来记录对应的<key,<key, value>>,用于查找现在的缓存中是否有key及其value,以及快速定位链表中的节点。

当需要获取数据时,先在哈希表中查找是否有对应的key,如果有,则返回对应的value,并将该节点移动到链表的表头。如果没有,则返回-1。

当需要写入数据时,先在哈希表中查找是否有对应的key,如果有,则更新对应的value,并将该节点移动到链表的表头。如果没有,则在链表的表头插入新的节点,并在哈希表中添加新的映射。如果此时缓存已满,则删除链表的最后一个节点,并在哈希表中删除对应的映射。

 

这道题的解题思路可以分为以下几个步骤:

  1. 创建一个双向链表和一个哈希表。
  2. 定义双向链表的节点类,包含key,value,pre和next四个属性。
  3. 定义双向链表的数据结构,包含head,tail,size三个属性,以及addFirst,remove,removeLast和size四个方法。
  4. 定义LRU缓存的数据结构,包含map,cache和cap三个属性,以及get和put两个方法。
  5. 在get方法中,先在map中查找key是否存在,如果存在,则返回value,并将节点移动到链表头部;如果不存在,则返回-1。
  6. 在put方法中,先在map中查找key是否存在,如果存在,则更新value,并将节点移动到链表头部;如果不存在,则在链表头部添加新节点,并在map中添加映射;如果缓存已满,则删除链表尾部的节点,并在map中删除映射。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
class LRUCache {
    //定义双向链表的节点类
    class Node{
        int key;
        int value;
        Node pre;
        Node next;
        public Node(int key,int value){
            this.key=key;
            this.value=value;
        }
    }
    //定义双向链表的数据结构
    class DoubleList{
        //头尾虚节点
        Node head,tail;
        //链表元素数
        int size;
        public DoubleList(){
            //初始化双向链表的数据
            head=new Node(0,0);
            tail=new Node(0,0);
            head.next=tail;
            tail.pre=head;
            size=0;
        }
        //在链表头部添加节点x,时间O(1)
        public void addFirst(Node x){
            x.next=head.next;
            x.pre=head;
            head.next.pre=x;
            head.next=x;
            size++;
        }
        //删除链表中的x节点(x一定存在)
        //由于是双链表且给的是目标Node节点,时间O(1)
        public void remove(Node x){
            x.pre.next=x.next;
            x.next.pre=x.pre;
            size--;
        }
        //删除链表中最后一个节点,并返回该节点,时间O(1)
        public Node removeLast(){
            if(tail.pre==head){
                return null;
            }
            Node last=tail.pre;
            remove(last);
            return last;
        }
        //返回链表长度,时间O(1)
        public int size(){return size;}
    }
 
    //key 映射到 Node(key,val)
    private HashMap<Integer,Node> map;
    //Node(k1,k2) 双向链表:k1 - k2 - ...
    private DoubleList cache;
    //最大容量
    private int cap;
 
    public LRUCache(int capacity) {
        map=new HashMap<>();
        cache=new DoubleList();
        this.cap=capacity;
    }
 
    /* 将某个key提升为最近使用的 */
    private void makeRecently(int key){
        Node x=map.get(key);
        //先从链表中删除这个节点
        cache.remove(x);
        //重新插到队头
        cache.addFirst(x);
    }
 
    /* 添加最近使用的元素 */
    private void addRecently(int key,int val){
        Node x=new Node(key,val);
        //链表头部就是最近使用的元素
        cache.addFirst(x);
        //别忘了在map中添加key的映射
        map.put(key,x);
    }
 
    /* 删除某一个key */
    private void deleteKey(int key){
        Node x=map.get(key);
        //从链表中删除
        cache.remove(x);
        //从map中删除
        map.remove(key);
    }
 
    /* 删除最久未使用的元素 */
    private void removeLeastRecently(){
       // 链表尾部的元素就是最久未使用的元素
       Node deleteNode=cache.removeLast();
       //别忘了从map中删除它的key
       int deletedKey=deleteNode.key;
       map.remove(deletedKey);
    }
 
    public int get(int key) {
       if(!map.containsKey(key)){
           return -1;
       }
       //将该数据提升为最近使用的
       makeRecently(key);
       return map.get(key).value;
 
    }
 
    public void put(int key, int value) {
      if(map.containsKey(key)){
          //删除旧的数据
          deleteKey(key);
          //新插入的数据为最近使用的数据
          addRecently(key,value);
          return ;
      }
      if(cap==cache.size()){
          //删除最久未使用的元素
          removeLeastRecently();
      }
      //添加为最近使用的元素
      addRecently(key,value);
 
    }
}

  

3. 总结

posted on   白露~  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示