LeetCode Interview Question 16.25. LRU Cache

Article directory

1. Title

  Design and build a "least recently used" cache that removes the least recently used items. The cache should map from keys to values ​​(allowing you to insert and retrieve values ​​for specific keys), with a maximum capacity specified at initialization time. When the cache fills up, it should remove the least recently used items.

  It should support the following operations: Get data get and Write data put.

  Get data get(key) - If the key exists in the cache, get the value of the key (always a positive number), otherwise return -1.
  Write data put(key, value) - If the key does not exist, its data value is written. When the cache capacity reaches its upper limit, it should delete the least recently used data values ​​before writing new data to make room for new data values.

Example:

LRUCache cache = new LRUCache( 2 /* cache capacity*/ );
cache.put(1, 1);
cache.put (2, 2);
cache.get(1); // Return 1
cache.put(3, 3); // This operation will cause Key 2 is invalid
cache.get(2); // Returns -1 (not found)
cache.put(4, 4); // The The operation will invalidate key 1
cache.get(1); // Return -1 (not found)
cache.get(3); // Return 3
cache.get(4); // Return 4

  Click here to jump to the question.

2. C# problem solution

  The idea of ​​LRU is to set the lowest priority for infrequently used elements. Therefore the algorithm rules are as follows:

  1. New data is inserted into the head of the linked list;
  2. When the cache hits (that is, the cached data is accessed), the data is moved to the head of the table;
  3. When the linked list is full, the data at the end of the linked list is discarded.

  The array is used to store the linked list structure here because it is simple and efficient.

public class LRUCache {
    
    
    private struct Node
    {
    
    
        public int Key, Value, Left, Right;
    }

    private int    _cap, _size;
    private Node[] _list; // 带头结点的双向链表数组实现,_list[0] 作为头结点

    private int FirstPos {
    
     // 第一个结点的物理位置存储在 _list[0].Right 中
        get => _list[0].Right;
        set => _list[0].Right = value;
    }

    private int RearPos {
    
     // 尾结点的物理位置存储在 _list[0].Left 中
        get => _list[0].Left;
        set => _list[0].Left = value;
    }

    private Dictionary<int, int> _dic;

    public LRUCache(int capacity) {
    
    
        _cap = capacity;
        _size = 0;
        _list = new Node[_cap + 1];        // _list[0] 用作 head 结点,存储 first 和 rear 位置
        _dic = new Dictionary<int, int>(); // 记录 key 所在结点的位置 pos
    }

    public int Get(int key) {
    
    
        // Cache 中存在 key,则将其移到表头,并返回对应的 value
        if (_dic.TryGetValue(key, out int pos)) {
    
    
            MoveToFirst(pos);
            return _list[pos].Value;
        }

        // 不存在,返回 -1
        return -1;
    }

    public void Put(int key, int value) {
    
    
        // Cache 中存在 key,则将其移到表头,并更新 value
        if (_dic.TryGetValue(key, out int pos)) {
    
    
            MoveToFirst(pos);
            _list[pos].Value = value;
        }
        // 不存在 key
        else {
    
    
            // 链表未满,则直接插入新结点
            if (_size < _cap) {
    
    
                AddNode(key, value, ++_size); // 新结点的物理位置在数组的下一个位置
                _dic.Add(key, _size);         // 添加 key 的记录
            }
            // 链表已满,需要移除尾结点,将新结点插入表头
            else {
    
    
                int rear = RearPos; // 记录此时的尾结点位置
                ReMoveAt(rear);     // 移除尾结点
                _dic.Remove(_list[rear].Key);
                AddNode(key, value, rear); // 向表头插入新结点,物理位置就存储在刚删掉的尾结点 rear 处
                _dic.Add(key, rear);
            }
        }
    }

    // 向表头插入结点,结点存储在 _list[pos] 的位置中
    private void AddNode(int key, int value, int pos) {
    
    
        // 创建结点
        _list[pos].Key = key;
        _list[pos].Value = value;

        // 插入表头
        _list[pos].Left = 0;
        _list[pos].Right = FirstPos;
        _list[FirstPos].Left = pos;
        FirstPos = pos;
    }

    // 将 pos 位置处的结点移到表头
    private void MoveToFirst(int pos) {
    
    
        ReMoveAt(pos);                                  // 将该结点从链表中移出
        AddNode(_list[pos].Key, _list[pos].Value, pos); // 再插入表头
    }

    // 将 _list[pos] 处的结点从链表中移除
    private void ReMoveAt(int pos) {
    
    
        int leftPos  = _list[pos].Left;
        int rightPos = _list[pos].Right;
        _list[leftPos].Right = rightPos;
        _list[rightPos].Left = leftPos;
    }
}

/**
 * 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);
 */
  • Time: 176 ms, beats 100.00% of users using C#
  • Memory: 64.35 MB, beats 85.71% of users using C#

Guess you like

Origin blog.csdn.net/zheliku/article/details/134469786