js 实现 LFU 算法

LFU 算法

 

 

/**
 * @param {number} capacity
 */
var LFUCache = function (capacity) {
    this.map = new Map();// 存放 key:node 的索引,便于快速访问节点
    this.freqArr = new Array() // 定义一个频次数组,存放一个双向链表
    this.capacity = capacity  // 可以保存的容量
    this.minFreq = 1 // 方便找到访问次数最小的双向链表,默认设置为 1
    this.freqArr.push(new DoubleLink())// 插入一个双向链表
    this.freqArr.push(new DoubleLink())// 插入一个双向链表,因为 minFreq 默认设置为了 1,所以要push两次
};

/** 
 * @param {number} key
 * @return {number}
 */
// 访问
LFUCache.prototype.get = function (key) {
    const node = this.map.get(key); // 获取双向链表的 node 节点
    if (node) {
        this._updateFreq(node) // 如果节点存在,则更新频次
        return node.value  // 并返回 node节点值
    }
    return -1 // 否则返回-1
};

/** 
 * @param {number} key 
 * @param {number} value
 * @return {void}
 */
// 设置
LFUCache.prototype.put = function (key, value) {
    if (this.capacity <=0 ) return  // 如果容量设置为<=0,则直接退出
    const node = this.map.get(key) // 获取 node 节点
    if (node) { 
        // 如果节点值存在,则更新频次
        this._updateFreq(node)
        // 并设置节点的值为最新的值
        node.value = value
        return true
    }
    // 如果当前容量>=设定容量
    if (this.map.size >= this.capacity) {
        // 获取当前最小频次下双向链表中的最后一个节点,并删除
        const lastNode = this.freqArr[this.minFreq].pop()
        // 删除 map 维护的该节点的 key 
        this.map.delete(lastNode.key)
    }
    // 创建新的双向链表节点
    const newNode = new DoubleLinkNode(key, value)
    // 由于是新创建节点,则第一次频次为 1,获取频次为 1 的双向链表,并在最前面插入新节点
    this.freqArr[1].unshift(newNode)
    // 不管之前最小频次到几,这里重置为 1
    this.minFreq = 1
    // map 中新增 key:node 映射
    this.map.set(key, newNode)
};
// 更新节点频次
LFUCache.prototype._updateFreq = function (node) {
    // 获取节点频次
    const freq = node.freq
    // 给节点中的频次变量+1
    node.incFreq() 
    // 删除原频次对应的双向链表中的该节点
    this.freqArr[freq].del(node)
    // 获取+1 后的频次数
    const newFreq = freq + 1
    // 如果频次数组对应的新的频次数的索引下没有节点
    if (!this.freqArr[newFreq]) {
        // 则给该频次数下赋值为新的双向链表
        this.freqArr[newFreq] = new DoubleLink()
    }
    // 否则,直接获取对应频次数下的双向链表,并在开头插入新节点
    this.freqArr[newFreq].unshift(node)
    // 最小访问次数等于freq,并且freq的双向链表指向为空,证明需要更新最小访问次数为最新的访问次数,避免原频次下节点删除后对应的节点已不存在
    if (freq === this.minFreq && this.freqArr[freq].size === 0) {
        this.minFreq = newFreq
    }
}
// 双向链表节点
var DoubleLinkNode = function (key, value) {
    this.key = key
    this.value = value
    this.freq = 1
    this.prev = null
    this.next = null
}
// 更新双向链表节点的频次数,+1
DoubleLinkNode.prototype.incFreq = function () {
    this.freq++
}
// 双向链表
var DoubleLink = function () {
    this.head = new DoubleLinkNode(-1, -1) // 创建头部节点
    this.tail = new DoubleLinkNode(-1, -1) // 创建尾部节点
    this.head.next = this.tail // 将头部节点的下一个节点指针指向尾部节点
    this.tail.prev = this.head // 将尾部节点的上一个节点指针指向头部节点
    this.size = 0 // 由于只有头尾,还没有插入节点,size 设为 0
}
// 双向链表的开头插入新节点方法
DoubleLink.prototype.unshift = function (node) {
    const tmp = this.head.next
    this.head.next = node
    node.next = tmp
    node.prev = this.head
    tmp.prev = node
    this.size++ // 记得更改节点长度
}
// 双向链表删除节点方法
DoubleLink.prototype.del = function (node) {
    const prev = node.prev
    const next = node.next
    prev.next = next
    next.prev = prev
    delete node.prev
    delete node.next
    this.size-- // 记得更改节点长度
}
// 双向链表从默认删除节点并返回该节点的方法
DoubleLink.prototype.pop = function () {
    const lastNode = this.tail.prev
    this.del(lastNode)
    return lastNode
}
源代码链接:https://leetcode.cn/problems/lfu-cache/solution/javascriptban-jie-ti-si-lu-by-ityou-o-vuw5/
本文主要给该代码增加了注释,便于理解。
转载请注明出处:https://www.cnblogs.com/beileixinqing/p/16669653.html
posted @ 2022-09-08 15:41  蓓蕾心晴  阅读(107)  评论(0编辑  收藏  举报