LeetCode实战:LRU缓存机制

我们两清 提交于 2019-11-26 19:34:13

背景


题目英文

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.

The cache is initialized with a positive capacity.

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

题目中文

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。

写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

进阶:

你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

算法实现

计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾位置。但问题是,删除哪些内容呢?我们肯定希望删掉哪些没什么用的缓存,而把有用的数据继续留在缓存里,方便之后继续使用。那么,什么样的数据,我们判定为「有用的」的数据呢?

LRU 缓存淘汰算法就是一种常用策略。LRU 的全称是 Least Recently Used,也就是说我们认为最近使用过的数据应该是是「有用的」,很久都没用过的数据应该是无用的,内存满了就优先删那些很久没用过的数据。

利用单链表的方式

public class LRUCache
{
    private readonly int _length;
    private readonly List<KeyValuePair<int, int>> _lst;

    public LRUCache(int capacity)
    {
        _length = capacity;
        _lst = new List<KeyValuePair<int, int>>();
    }

    private int GetIndex(int key)
    {
        for (int i=0,len=_lst.Count;i<len;i++)
        {
            if (_lst[i].Key == key)
            {
                return i;
            }
        }
        return -1;
    }

    public int Get(int key)
    {
        int index = GetIndex(key);
        if (index!=-1)
        {
            int val = _lst[index].Value;
            _lst.RemoveAt(index);
            _lst.Add(new KeyValuePair<int, int>(key, val));
            return val;
        }
        return -1;
    }

    public void Put(int key, int value)
    {
        int index = GetIndex(key);
        if (index!=-1)
        {
            _lst.RemoveAt(index);
        }
        else if (_lst.Count == _length)
        {
            _lst.RemoveAt(0);
        }
        _lst.Add(new KeyValuePair<int, int>(key, value));
    }
}

/**
 * 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);
 */

利用 字典(哈希)+单链表 的方式

public class LRUCache
{
    private readonly List<int> _keys;
    private readonly Dictionary<int, int> _dict;


    public LRUCache(int capacity)
    {
        _keys = new List<int>(capacity);
        _dict = new Dictionary<int, int>(capacity);
    }

    public int Get(int key)
    {
        if (_dict.ContainsKey(key))
        {
            _keys.Remove(key);
            _keys.Add(key);
            return _dict[key];
        }
        return -1;
    }

    public void Put(int key, int value)
    {
        if (_dict.ContainsKey(key))
        {
            _dict.Remove(key);
            _keys.Remove(key);
        }
        else if (_keys.Count == _keys.Capacity)
        {
            _dict.Remove(_keys[0]);
            _keys.RemoveAt(0);
        }
        _keys.Add(key);
        _dict.Add(key, value);
    }
}

/**
 * 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);
 */

实验结果

利用单链表的方式

  • 状态:通过
  • 18 / 18 个通过测试用例
  • 执行用时: 868 ms, 在所有 C# 提交中击败了 6.25% 的用户
  • 内存消耗: 47.8 MB, 在所有 C# 提交中击败了 26.67% 的用户

提交结果

利用 字典(哈希)+单链表 的方式

  • 状态:通过
  • 18 / 18 个通过测试用例
  • 执行用时: 392 ms, 在所有 C# 提交中击败了 76.56% 的用户
  • 内存消耗: 47.9 MB, 在所有 C# 提交中击败了 20.00% 的用户

提交结果


相关图文

1. “数组”类算法

2. “链表”类算法

3. “栈”类算法

4. “队列”类算法

5. “递归”类算法

6. “位运算”类算法

7. “字符串”类算法

8. “树”类算法

9. “哈希”类算法

10. “排序”类算法

11. “搜索”类算法

12. “动态规划”类算法

13. “回溯”类算法

14. “数值分析”类算法

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!