Design - 146. LRU Cache

时间:2022-07-25
本文章向大家介绍Design - 146. LRU Cache,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
  1. LRU Cache

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 Cache,提供get和put方法,时间复杂度都控制在O(1),get操作要求时间复杂度为O(1),说明肯定有一个hash的结构用来查找,put方法也是O(1)可以使用链表,这里使用双向链表,实现删除最后一个元素和把元素移动到链表头。对于链表类题目,最好的就是使用dummy节点,这样可以避免很多空针或者断链的情况发生。

代码:

go:

type LRUCache struct {
    size int
    capacity int
    head *Node
    tail *Node
    m map[int]*Node
}

type Node struct {
    key int
    val int
    prev *Node
    next *Node
}

func Constructor(capacity int) LRUCache {
    var lru =  LRUCache{
        capacity:capacity,
        head: &Node{},
        tail: &Node{},
        m:make(map[int]*Node),
    }
    lru.head.next = lru.tail
    lru.tail.prev = lru.head
    
    return lru
}

func (this *LRUCache) Get(key int) int {
    target, ok := this.m[key]
    if !ok {
        return -1
    } else {
        // moveToHead
        this.moveToHead(target)
    }
    return target.val
}

 

func (this *LRUCache) Put(key int, value int) {
    if target, ok := this.m[key]; ok {
        // 更新target节点的值,然后把当前节点移动到链表头
        target.val = value
        this.moveToHead(target)
    } else {
        // 新生成一个节点,添加到链表头
        node := &Node{key:key, val:value}
        if this.size == this.capacity{
            this.removeTail()
        }
        this.addToHead(node)
    }
}

// 删除最后一个数据节点
func (this *LRUCache) removeTail() {
    if len(this.m) == 0 {
        return 
    }
    
    delete(this.m, this.tail.prev.key)
    this.size--
    
    // remove tail
    this.tail.prev.prev.next = this.tail
    this.tail.prev = this.tail.prev.prev
}

// 从链表头添加新节点
func (this *LRUCache) addToHead(node *Node) {
    temp := this.head.next

    this.head.next = node
    node.prev = this.head    
    node.next = temp
    temp.prev = node

    this.m[node.key]=node
    this.size++
}

// 把节点移动到链表头
func (this *LRUCache) moveToHead(node *Node) {   
    // 把node摘出来
    node.prev.next = node.next    
    node.next.prev = node.prev
    this.size--
    
    // 再放到链表头
    this.addToHead(node)
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * obj := Constructor(capacity);
 * param_1 := obj.Get(key);
 * obj.Put(key,value);
 */