146. LRU 缓存机制

题意

通过哈希表和双向链表实现LRU机制
什么是LRU:https://zhuanlan.zhihu.com/p/34133067

题解

思路:只要想清楚LRU的过程就好

  • GET:
    • 不存在:直接返回-1
    • 存在:将node移到head,返回value
  • PUT:
    • 存在:将value更新,将node移到head
    • 不存在:新建node,更新hash,将node插到head,再判断len有没有超出,超出则移除tail
  • 注意点:
    • 为什么需要head和tail节点:不用再考虑左右越界问题
    • 为什么双向链表需要存key:在删除尾节点的时候需要根据节点的key删除hash中的元素

复杂度:

  • 时间复杂度:O(1)
  • 空间复杂度:O(容量) ```go type LRUCache struct { cache map[int]DLinkedNode head, tail DLinkedNode len, cap int }

type DLinkedNode struct { key, value int prev, next *DLinkedNode }

func initDLinkedNode(key, value int) *DLinkedNode { return &DLinkedNode{ key: key, value: value, } }

func (this LRUCache) addToHead(node DLinkedNode) { node.prev = this.head node.next = this.head.next this.head.next.prev = node this.head.next = node }

func (this LRUCache) removeNode(node DLinkedNode) { node.prev.next = node.next node.next.prev = node.prev }

func (this LRUCache) moveToHead(node DLinkedNode) { this.removeNode(node) this.addToHead(node) }

func (this LRUCache) removeTail() DLinkedNode { node := this.tail.prev this.removeNode(node) return node }

func Constructor(capacity int) LRUCache { lru := LRUCache{ cache: map[int]*DLinkedNode{}, head: initDLinkedNode(0, 0), tail: initDLinkedNode(0, 0), cap: capacity, } lru.head.next = lru.tail lru.tail.prev = lru.head return lru }

func (this *LRUCache) Get(key int) int { node, ok := this.cache[key] if !ok { return -1 } this.moveToHead(node) return node.value }

func (this *LRUCache) Put(key int, value int) { node, ok := this.cache[key] if ok { node.value = value this.moveToHead(node) return } newNode := initDLinkedNode(key, value) this.cache[key] = newNode this.addToHead(newNode) this.len++ if this.len > this.cap { tailNode := this.removeTail() delete(this.cache, tailNode.key) this.len— } }

``` 结果:

  • 执行用时:112 ms, 在所有 Go 提交中击败了94.04%的用户
  • 内存消耗:11.6 MB, 在所有 Go 提交中击败了90.58%的用户