乐趣区

关于leetcode个人解题总结:146LRU-缓存-算法leetcode附思维导图-全部解法300题

零 题目:算法(leetcode,附思维导图 + 全副解法)300 题之(146)LRU 缓存

一 题目形容


二 解法总览(思维导图)

三 全副解法

1 计划 1

1)代码:

// 计划 1“本人。哈希法”。// 技巧:遇到 O(1) 的 get、put 操作,优先思考 哈希表(JS 里的 Map 数据结构)。/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
    this.capacity = capacity;
    this.curCapacity = 0;
    // 注:越是最近应用的 key,就越往 map 的前面放!this.map = new Map();};

/** 
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {const {map} = this;
    let resVal = -1;
    
    // 边界 1:get 操作,若 曾经有该 key,// 则 先删该 key、接着把该 key 放到 map 的最初 —— 示意最近应用过该 key!if (map.has(key)) {resVal = map.get(key);
        map.delete(key);
        map.set(key, resVal);
    }
    
    return resVal;
};

/** 
 * @param {number} key 
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {const {capacity, curCapacity, map} = this;

    // 边界 2:put 操作,若 曾经有该 key,// 则 先删该 key、接着把该 key 放到 map 的最初 —— 示意最近应用过该 key!if (map.has(key)) {map.delete(key);
        map.set(key, value);
        return;
    }

    // 边界 3:put 操作,若 以后曾经放不下了(即 curCapacity >= capacity),// 则 删除 map 的第一个 key 且 将新 key 放到 map 的最初 —— 示意最近应用过该 key!if (curCapacity >= capacity) {for (const [key, val] of map) {map.delete(key);
            break;
        }
        map.set(key, value);
    }
    // 边界 4:put 操作,若 以后放得下(即 curCapacity < capacity),// 则 间接将新 key 放到 map 的最初 —— 示意最近应用过该 key 且 this.curCapacity++。else {map.set(key, value);
        this.curCapacity++;
    }
    // 注:以上 5 行,能够合并成如下 2 行(但为了可读性,可分拆为 if、else 2 分支):// }
    // map.set(key, value);
};

2 计划 2

1)代码:

// 计划 2“本人。哈希表 + 双向链表”。// 参考:// 1)https://leetcode.cn/problems/lru-cache/solution/146-lru-huan-cun-ji-zhi-by-chen-wei-f-tl1n/

// 思路:// 1)重点实现 ListNode 和 LRUCache 2 个类。// 2)get、put 操作:// 依据当前情况进行 curCapacity、map、各种节点 的信息变更即可,详见代码的实现。// 双向链表的单个节点
class ListNode {constructor(key, value) {
        this.key = key;
        this.value = value;
        // 指向后一个节点
        this.next = null;
        // 指向前一个节点
        this.prev = null;
    }
}

class LRUCache {constructor(capacity) {
        this.capacity = capacity;
        this.curCapacity = 0;

        this.map = new Map();

        this.dummyHead = new ListNode();
        this.dummyTail = new ListNode();
        this.dummyHead.next = this.dummyTail;
        this.dummyTail.prev = this.dummyHead;
    }

    get(key) {const {map = new Map()} = this,
            node = map.get(key);
        
        // 没找到
        if (!node) {return - 1;}
        // 找到,将该节点挪到头部,并返回相应的值
        this.moveToHead(node);
        return node.value;
    }

    put(key, value) {const {map = new Map()} = this,
            node = map.get(key);
        
        if (!node) {
            // 不存在该节点,则进行节点的创立。const newNode = new ListNode(key, value);
            map.set(key, newNode);
            // 退出头结点(addToHead),而不是挪(moveToHead)this.addToHead(newNode);
            this.curCapacity++;
            // 边界:超容量了,得删除
            if (this.curCapacity > this.capacity) {this.removeLRUItem();
            }
        }
        else {
            // 存在该节点,更新其值 并 将该节点挪到头部
            node.value = value;
            this.moveToHead(node);
        }
    }

    moveToHead(node) {
        // 从链表中删除该节点 并 将该节点增加至头结点
        this.removeFromList(node);
        this.addToHead(node);
    }

    // 节点的删除(实质:指针指向的变更)removeFromList(node) {
        let nodePre = node.prev,
            nodeNext = node.next;
        
        nodePre.next = nodeNext;
        nodeNext.prev = nodePre;
    }

    // 节点退出链表头部 的指针操作
    addToHead(node) {const {dummyHead = null} = this,
            dummyHeadNext = dummyHead.next;

        node.prev = dummyHead;
        node.next = dummyHeadNext;
        dummyHeadNext.prev = node;
        dummyHead.next = node;
        // 注:下面 4 行代码等价于上面 1 行(JS 的语法糖)// [node.prev, node.next, dummyHeadNext.prev, dummyHead.next] = [dummyHead, dummyHeadNext, node, node];
    }

    removeLRUItem() {const {dummyTail = null, map = new Map()} = this,
            tailNode = dummyTail.prev;
        
        // 删除尾结点 并更新容量(即 curCapacity)信息
        this.removeFromList(tailNode);
        map.delete(tailNode.key);
        this.curCapacity--;
    }
}

四 资源分享 & 更多

1 历史文章 – 总览

2 博主简介

码农三少,一个致力于编写 极简、但齐全题解(算法 )的博主。
专一于 一题多解、结构化思维,欢送一起刷穿 LeetCode ~

退出移动版