<
>

JavaScript双向链表实现LFU缓存算法

2022-04-16 16:18:00 来源:易采站长站 作者:

目录
什么是LFU描述解题思路1、构造节点结构体2、构造双向链表3、编写链表头添加节点方法4、编写删除节点方法5、构造LRU缓存结构体6、编写get方法7、编写put方法

什么是LFU

LFU(Least Frequently Used),最近最少使用策略,也就是说在一段时间内,数据被使用频次最少的,优先被淘汰。
它是一种用于管理计算机内存的缓存算法,采用LFU算法的最简单方法是为每个加载到缓存的块分配一个计数器。每次引用该块时,计数器将增加一。当缓存达到容量并有一个新的内存块等待插入时,系统将搜索计数器最低的块并将其从缓存中删除。

描述

请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。

实现 LFUCache 类:

LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象

int get(int key) - 如果键 key 存在于缓存中,则获取键的值,否则返回 -1 。

void put(int key, int value) - 如果键 key 已存在,则变更其值;如果键不存在,请插入键值对。

当缓存达到其容量 capacity 时,则应该在插入新项之前,移除最不经常使用的项。

在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近最久未使用 的键。

为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。

当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作,使用计数器的值将会递增。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

解题思路

1、构造节点结构体

保存对应的数据信息

const LFUNode = function ( key = "", val = "", freq = 0, pre = null, next = null) { this.key = key; this.val = val; this.freq = freq; this.pre = pre; this.next = next;};

2、构造双向链表

构造带有头尾节点的双向链表,head和tail为哨兵节点,不保存信息,仅用于标记头尾。

const LFULinkLine = function (node) { let head = new LFUNode("head"); let tail = new LFUNode("tail"); head.next = node; tail.pre = node; node.pre = head; node.next = tail; this.head = head; this.tail = tail;};

3、编写链表头添加节点方法

将节点插入到链表的头结点之后,其他节点往后移。

LFULinkLine.prototype.addNode = function (node) { this.head.next.pre = node; node.pre = this.head; node.next = this.head.next; this.head.next = node;};

4、编写删除节点方法

将节点从链表中删除。

LFULinkLine.prototype.removeNode = function (node) { node.pre.next = node.next; node.next.pre = node.pre;};

5、构造LRU缓存结构体

构造LRU缓存结构体,具体信息如下代码,capacity用于保存最大缓存数,即该类的容量;num保存当前存储的数据量,用于判别是否超出最大容量;minFreq保存当前存储的数据中的最小频率,删除的时候需要优先删除频率最小的;kvMap保存节点详细信息,索引为节点的key值,查询可以直接从这里查出信息;freqMap保存对应频率的链表信息,索引为节点的freq(频率),删除的时候可以快速从这里获取需要删除节点的信息。

/*** @param {number} capacity*/var LFUCache = function (capacity) { this.capacity = capacity;//最大缓存 this.num = 0;//当前数目 this.minFreq = 0;//当前最小频率 this.kvMap = new Map();//保存节点信息 this.freqMap = new Map();//保存对应频率的链表信息};

6、编写get方法

get主要有以下两种情况:

(1)节点存在

通过kvMap获取到对应节点将节点从freqMap中删除判断是否需要修改minFreq修改节点的freq重新将节点插入freqMap返回节点的value

(2)节点不存在
容量capacity为0或者kvMap中没有该节点信息,直接返回-1即可。

/*** @param {number} key* @return {number}*/LFUCache.prototype.get = function (key) { if (this.capacity === 0) return -1; if (!this.kvMap.has(key)) return -1; //通过kvMap获取到对应节点 let node = this.kvMap.get(key); let linkLine = this.freqMap.get(node.freq); //将节点从freqMap中删除 linkLine.removeNode(node); //判断是否需要修改minFreq //清空了 if (linkLine.head.next === linkLine.tail) {  this.freqMap.delete(node.freq);  if (this.minFreq == node.freq) this.minFreq++; } //修改节点的freq node.freq++; //重新将节点插入freqMap if (this.freqMap.has(node.freq)) {  linkLine = this.freqMap.get(node.freq);  linkLine.addNode(node); } else {  this.freqMap.set(node.freq, new LFULinkLine(node)); } //返回节点的value return node.val;};

7、编写put方法

put操作主要有以下两种情况:

(1)更新

通过kvMap获取到对应节点将节点从freqMap中删除判断是否需要修改minFreq更新节点信息重新将节点插入freqMap

(2)存入

存入情况下又有两种情况:

容量已满

通过minFreq找到需要删除的节点将节点从freqMap中删除判断是否需要修改minFreq将新节点插入freqMap更新minFreq

容量未满

修改num将新节点插入freqMap更新minFreq
/*** @param {number} key* @param {number} value* @return {void}*/LFUCache.prototype.put = function (key, value) { if (this.capacity === 0) return; if (this.kvMap.has(key)) {  //更新  //通过kvMap获取到对应节点  let node = this.kvMap.get(key);  //将节点从freqMap中删除  let linkLine = this.freqMap.get(node.freq);  linkLine.removeNode(node);  //判断是否需要修改minFreq  if (linkLine.head.next === linkLine.tail) {   if (this.minFreq == node.freq) this.minFreq++;   this.freqMap.delete(node.freq);  }  //更新节点信息  node.val = value;  node.freq++;  //重新将节点插入freqMap  if (this.freqMap.has(node.freq)) {   linkLine  if (this.capacity == this.num) {   //存满   let freq = this.minFreq;   let linkLine = this.freqMap.get(freq);   let node = linkLine.tail.pre;   linkLine.removeNode(node);   this.kvMap.delete(node.key);   if (linkLine.head.next === linkLine.tail) {    this.freqMap.delete(node.freq);   }  } else {   this.num++;  }  let node = new LFUNode(key, value, 0);  this.kvMap.set(key, node);  if (this.freqMap.has(0)) {   let linkLine = this.freqMap.get(0);   linkLine.addNode(node);  } else {   let linkLine = new LFULinkLine(node);   this.freqMap.set(0, linkLine);  }  this.minFreq = 0; }};/*** Your LFUCache object will be instantiated and called as such:* var obj = new LFUCache(capacity)* var param_1 = obj.get(key)* obj.put(key,value)*/

暂时禁止评论

微信扫一扫

易采站长站微信账号