在我第一次写LRU时,应用map+列表的模式,map使得get和set的工夫复杂度为O(1),列表保护插入元素的程序,当get或set元素时,将元素挪动或插入到队头;当达到LRU缓存容量下限时,将列表尾部元素去除掉。

然而在列表中调整元素程序时,工夫复杂度达不到O(1)。

明天写了一个改进版,应用map+双向链表的模式。map存储key和链表节点的指针,双向链表中既存储key也存储value。map仍然用来使get和set的工夫复杂度为O(1),当须要将元素挪动到队头时,仅需通过map找到节点,将节点左右连贯打断,而后插入到队头,当删除元素或队头插入元素时,仅需找到头尾节点再进行插入或删除就能够了。这样取代列表后,工夫复杂度变为O(1)。

援用一张图来阐明这个构造。

操作流程:

get操作:

  • 在map中存在:将元素挪动至链表头节点,返回value值
  • 在map中不存在:返回-1

set操作:

  • 在map中存在:批改节点中value的值,而后将节点挪动到链表头节点
  • 在map中不存在:
    • 没有达到cap下限:构建节点,map中存储此节点,而后将节点插入到头节点
    • 达到cap下限:先删除链表队尾元素,删除map中的key。而后构建节点,map中存储此节点,而后将节点插入到头节点

代码:

package mainimport "fmt"// map用来get 和 set,双向链表放弃绝对程序// 为什么用双向链表?因为在删除节点时,以及插入到头部时,工夫复杂度都是1type Node struct {    pre *Node    next *Node    key int // 不便删除map中的key    value int //}type lruCache struct {    cap int  // 存储容量,达到容量后再插入则须要删除元素    headNode *Node  // 双向链表头节点,    tailNode *Node  // 双向链表尾节点,    nodeMap map[int]*Node  // 使得get、set操作工夫复杂度为1}func (l *lruCache) get(k int) int {    node := l.nodeMap[k]    if node == nil{        return -1    }    // 获取元素,并将此元素挪动到head头地位    headNode := l.headNode    // 将节点截取    node.pre.next = node.next    node.next.pre = node.pre    // 而后将此节点插入到头节点    headNode.next.pre = node    node.next = headNode.next    headNode.next = node    node.pre = headNode    v := node.value    return v}func (l *lruCache) set(k, v int) {    node := l.nodeMap[k]    // 第一种状况,k在map中不存在,直接插入到头节点    if node == nil{        // 思考lru的容量,达到容量后则要删除尾部元素(间接让尾部元素的指针断开)        if len(l.nodeMap) == l.cap {            lastNode := l.tailNode.pre            lastNode.pre.next = l.tailNode            l.tailNode.pre = lastNode.pre            lastNode.pre = nil            lastNode.next = nil            // map中也要删除            deleteKey := lastNode.key            delete(l.nodeMap, deleteKey)        }        newNode := &Node{            pre:   l.headNode,            next:  l.headNode.next,            key:   k,            value: v,        }        l.headNode.next = newNode        newNode.next.pre = newNode        // map中设置值        l.nodeMap[k] = newNode    }else{        // 第二种状况,key在map中存在。将map的值更改,而后将此值挪动到头部        node.value = v        // 将节点截取        node.pre.next = node.next        node.next.pre = node.pre        // 而后将此节点插入到头节点        firstNode := l.headNode        secondNode := l.headNode.next        firstNode.next = node        secondNode.pre = node        node.next = secondNode        node.pre = firstNode    }}func main() {    head := &Node{        pre: nil,        next: nil,        key: 0,        value: 0,    }    tail := &Node{        pre: nil,        next: nil,        key: 0,        value: 0,    }    head.next = tail    tail.pre = head    lru := lruCache{        cap: 2,        headNode: head,        tailNode: tail,        nodeMap: make(map[int]*Node),    }    lru.set(1,1)    lru.set(2, 2)    re := lru.get(1)    fmt.Println(re)  // 1    lru.set(3,3)    re = lru.get(2)    fmt.Println(re)  // -1    re = lru.get(3)    fmt.Println(re)  // 3    lru.set(4, 4)    re = lru.get(1)    fmt.Println(re)  // -1    re = lru.get(3)    fmt.Println(re)  // 3    re = lru.get(4)    fmt.Println(re)  // 4}