算法学习记录(一零一)

118 阅读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) 的平均时间复杂度运行。

image.png

解:

  1. 使用哈希表记录数据,使用双向链表记录值的使用顺序,哈希表的key代表值的key,val代表值对应的节点。节点保存值的key和val
  2. 创建占位头尾节点,便于直接获取到此时的头尾节点,越接近头节点的就是越近使用的,反之就是越久不适用的
  3. 存值时如果表中无此数据,那么生成一个节点,根据头节点可以得到最近使用值的节点,把新生成的节点插入头节点和最近使用值节点之间。并把这个节点值更新。如果此时哈希表大小过量,那么把尾节点的上一个节点从链表中移除并把表中对应记录删除。
  4. 存值时如果表中有数据,那么取出哈希表中该key对应的节点并把val更新,把这个节点从原来位置脱离并插入头节点的后一位
  5. 取值时的移动节点操作和4步骤一样,然后把表中数据返回
class LRUCache {
    private capacity: number
    private hashMap: Map<number, any>
    // 占位头节点,方便更新真正的头节点
    private headNode: any
    // 占位尾节点,方便找到真正的尾节点
    private tailNode: any
    constructor(capacity: number) {
        this.capacity = capacity
        this.hashMap = new Map()
        this.headNode = this.node(0,0) 
        this.tailNode = this.node(0,0)
        this.headNode.next = this.tailNode
        this.tailNode.pre = this.headNode
    }

    moveNode (curNode: any): void {
        const oldNode = this.headNode.next 
        if (oldNode === curNode) return
        // 将cur脱离原来位置
        curNode.pre.next = curNode.next 
        curNode.next.pre = curNode.pre
        // 将cur放到链表头部
        oldNode.pre = curNode
        curNode.pre = this.headNode
        this.headNode.next = curNode
        curNode.next = oldNode
    }

    get(key: number): number {
        const keyVal = this.hashMap.get(key)
        if (keyVal) {
            this.moveNode(keyVal)
            return keyVal.val
        }
        return -1
    }

    put(key: number, value: number): void {
        const keyVal = this.hashMap.get(key)
        if (keyVal) {
            // 获取双向链表中的节点
            keyVal.val = value
            // 更新节点位置
            this.moveNode(keyVal)
        } else {
            // 创建一个新节点
            const curNode = this.node(key, value)
            // 把节点一起存入map,用来获取在双向链表中的对应节点
            this.hashMap.set(key, curNode)
            // 获取老的最新节点
            const oldNode = this.headNode.next
            // 更新节点指向
            oldNode.pre = curNode
            curNode.pre = this.headNode
            this.headNode.next = curNode
            curNode.next = oldNode
            // 过量则删除尾节点对应数据
            if (this.hashMap.size > this.capacity) {
                this.hashMap.delete(this.tailNode.pre.key)
                this.tailNode.pre = this.tailNode.pre.pre    
                this.tailNode.pre.next = this.tailNode  
            }
        } 
    }

    node (key: number, val: number) {
        return {
            pre: null,
            next: null,
            val,
            key
        }
    }
}