LeetCode系列146—LRU缓存机制

题意

146. LRU 缓存机制

题解

方法一:哈希表 + 双向链表

class LRUCache {
    
    

    HashMap<Integer, Node> map;
    DoubleList cache;
    int cap;

    public LRUCache(int capacity) {
    
    
        map = new HashMap();
        cache = new DoubleList();
        this.cap = capacity;
    }
    
    public int get(int key) {
    
    
        if(map.containsKey(key)){
    
    
            makeRecently(key); // 先将key标记为最近使用,再返回value
            return map.get(key).val;
        }else return -1;
    }
    
    public void put(int key, int value) {
    
    
        if(map.containsKey(key)){
    
    
            deleteKey(key); // 从原map中移除该key
            addRecently(key, value); // 更新最近使用
            return;
        }
        int size = cache.getSize();
        if(size == cap){
    
     // 说明需要移除最久未使用的元素了
            removeLeastRecently();
        }
        // 走到这说明添加的是一个新元素
        addRecently(key, value);
    }

    public void makeRecently(int key){
    
     // 将某个key标记为最近使用的元素(map中已存在的)
        Node x = map.get(key);
        cache.remove(x); // 先从双链表删除
        cache.addLast(x); // 再添加到链表末尾, 因为尾部是最近使用过的元素
    }

    public void addRecently(int key, int value){
    
     // 添加最近使用过的元素, 原map中不存在该key
        Node x = new Node(key, value);
        cache.addLast(x);
        map.put(key, x); // 同时记得更新map
    }
    
    public void deleteKey(int key){
    
     // 删除某一个key对应的元素
        Node x = map.get(key);
        map.remove(key);
        cache.remove(x); // 在map中和cache中同时删除
    }
    
    public void removeLeastRecently(){
    
     // 删除最久未使用的元素
        Node oldNode = cache.removeHead(); // 最久未使用的一定在链表头部
        int oldKey = oldNode.key;
        map.remove(oldKey);
    }
}


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;
    }

    public void addLast(Node x){
    
     // 添加到链表尾部,且越靠近链表尾部,越代表最近使用过
        // 如当前链表为: head <-> 1 <-> tail,加入结点x = 2
        x.pre = tail.pre;
        x.next = tail; // 完成结点2指向两端的箭头  head <-> 1 <- 2 -> tail; 此时tail.pre = 结点1还未断开
        tail.pre.next = x;
        tail.pre = x; // 完成由结点2两端指向结点2的箭头  head <-> 1 <-> 2 <-> tail;
        size ++; // 更新链表长度
    }

    public void remove(Node x){
    
     // 删除指定结点(该结点一定存在与链表)
        x.pre.next = x.next;
        x.next.pre = x.pre;
        size--;
    }
    
    public Node removeHead(){
    
     // 删除并返回头结点
        if(head.next == tail) return null; // 说明是空链表
        Node first = head.next;
        remove(first); // size在remove中更新
        return first;  // 用作在哈希表中移除最久未使用的数据值
    }

    public int getSize(){
    
     // 获取链表长度
        return size;
    }
    
}
class Node{
    
     
    int key, val; // 存储哈希表的key和value 
    Node pre, next; //前后指针
    
    public Node(int key, int value){
    
    
        this.key = key;
        this.val = value;
        
    }
}
struct DListNode {
    
    
  int key, value;
  DListNode* prev;
  DListNode* next;
  DListNode(): key(0), value(0), prev(nullptr), next(nullptr) {
    
    }
  DListNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {
    
    }
};

class DList {
    
    
public:
  DList() {
    
    
    // 使用伪头部和伪尾部节点
    head = new DListNode();
    tail = new DListNode();
    head->next = tail;
    tail->prev = head;
  }

  void push_front(DListNode* node) {
    
    
    node->prev = head;
    node->next = head->next;
    head->next->prev = node;
    head->next = node;
  }

  void remove_node(DListNode* node) {
    
    
    node->prev->next = node->next;
    node->next->prev = node->prev;
  }

  void move_to_head(DListNode* node) {
    
    
    remove_node(node);
    push_front(node);
  }

  DListNode* remove_tail() {
    
    
    DListNode* node = tail->prev;
    remove_node(node);
    return node;
  }

private:
  DListNode* head;
  DListNode* tail;
};

class LRUCache {
    
    
private:
  unordered_map<int, DListNode*> cache;
  DList *dList;
  int size;
  int capacity;

public:
  LRUCache(int _capacity): capacity(_capacity), size(0) {
    
    
    dList = new DList();
  }

  int get(int key) {
    
    
    if (!cache.count(key)) {
    
    
      return -1;
    }
    // 如果 key 存在,先通过哈希表定位,再移到头部
    DListNode* node = cache[key];
    dList->move_to_head(node);
    return node->value;
  }

  void put(int key, int value) {
    
    
    if (!cache.count(key)) {
    
    
      // 如果 key 不存在,创建一个新的节点
      DListNode* node = new DListNode(key, value);
      // 添加进哈希表
      cache[key] = node;
      // 添加至双向链表的头部
      dList->push_front(node);
      ++size;
      if (size > capacity) {
    
    
        // 如果超出容量,删除双向链表的尾部节点
        DListNode* removed = dList->remove_tail();
        // 删除哈希表中对应的项
        cache.erase(removed->key);
        // 防止内存泄漏
        delete removed;
        --size;
      }
    }
    else {
    
    
      // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
      DListNode* node = cache[key];
      node->value = value;
      dList->move_to_head(node);
    }
  }
};

参考

LRU缓存机制

猜你喜欢

转载自blog.csdn.net/younothings/article/details/120170126