leetcode146LRU缓存

50 阅读2分钟

146. LRU 缓存

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

思路:

由于题目给出的要求是 get和put必须以O(1)的时间复杂度运行 且这个值是键值对的形式,那么自然而然的会想到用hash表之类的结构来实现。

同时需要实现  **逐出** 最久未使用的关键字 考虑使用双向链表实现。

每次get的时候读取值之后将 关键字 放到表头,那么链表的最后一个就是最久未使用的关键字。而双向链表可以方便的读取尾节点而不用遍历整个链表

代码实现如下:

// 链表结构 双向链表好删除尾节点
class Node {
  constructor(key = 0, value = 0) {
    this.key = key
    this.value = value
    this.prev = null
    this.next = null
  }
}

// 初始化 限制最长为capacity
// 创建一个hashmap储存key和node 方便读取
// 创建哨兵节点保证指针永不只指向空
/**
 * @param {number} capacity
 */
class LRUCache {
  constructor(capacity) {
    this.capacity = capacity
    // 创建一个哨兵结点 并创建双向链表
    this.sentinel = new Node()
    this.sentinel.next = this.sentinel
    this.sentinel.prev = this.sentinel
    this.hashmap = new Map()
  }

  /**
   * 取出node后将其放到链表头
   * @param {*} key 
   * @returns 
   */
  getNode(key) {
    if (!this.hashmap.has(key)) {
      return null
    }
    let node = this.hashmap.get(key)
    this.removeNode(node)
    this.pushFront(node)
    return node
  }

  /**
   * 删除结点
   * @param {*} node 
   */
  removeNode(node) {
    node.next.prev = node.prev
    node.prev.next = node.next
  }

  /**
   * 把结点添加到链表头 哨兵之后
   * @param {*} node 
   */
  pushFront(node) {
    node.prev = this.sentinel
    node.next = this.sentinel.next
    this.sentinel.next = node
    node.next.prev = node
  }

  /** 
   * @param {number} key
   * @return {number}
  */
  get(key) {
    let node = this.getNode(key)
    return node ? node.value : -1
  }

  /**
   * hash表中查找 如果存在的话就更改值 不存在的话就set进hash表中
   * @param {*} key 
   * @param {*} value 
   */
  put(key, value) {
    let node = this.getNode(key)
    if (node) {
      node.value = value
      return
    }
    node = new Node(key, value)
    this.pushFront(node)
    this.hashmap.set(key, node)
    if (this.hashmap.size > this.capacity) {
      const lastNode = this.sentinel.prev
      this.removeNode(lastNode)
      this.hashmap.delete(lastNode.key)
    }
  };
}


/**
 * Your LRUCache object will be instantiated and called as such:
 * var obj = new LRUCache(capacity)
 * var param_1 = obj.get(key)
 * obj.put(key,value)
 */