Day17 如何通过链表做LRU/LFU缓存?

单链表
链表就是把零散的节点(node)串联起来的一种数据结构。在每个节点里,会有两个核心元素,一个是数据,一个是下一个节点的地址,我们称之为后继指针(next)

class Node {
  constructor(data){
    this.data = data;
    this.next = null;
  }
}
class LinkedList {
  constructor(){
    this.head = null;
    this.size = 0;
  }
  isEmpty(){ /*判断是否为空*/ }
  size() { /*获取链表大小*/ }
  getHead() { /*获取链表头节点*/ } 
  indexOf(element) { /*获取某个元素的位置*/ } 
  insertAtTail(element) { /*在表尾插入元素*/ }
  insertAt(element, index) { /*在指定位置插入*/ }
  remove(element) { /*删除某个元素*/ }
  removeAt(index) { /*在指定位置删除*/ }
  getElementAt(index) { /*根据某个位置获取元素*/ }
  removeAtHead(){ /*在表头位置删除元素*/ }
}

双链表
在单链表基础上增加一个上一节点指针的属性。同样的,双链表也可以基于单链表扩展,在里面加一个表尾节点。对于从后往前的遍历,和从前往后的遍历一样,复杂度都是 O(n)。

class DoublyNode extends Node { 
  constructor(data, next, prev) {
    super(data, next); 
    this.prev = prev; 
  }
}

class DoublyLinkedList extends LinkedList {
  constructor() {
    this.tail = undefined;
  }
}

循环链表
头尾相连的链表。

双向循环链表的话,就是除了顺时针的头尾相接以外,从逆时针也可以循环的双循环链表。

如何通过链表来做缓存
最近最少使用(LRU,least recently used)和最不经常使用(LFU,least frequently used),简称 LRU 缓存和 LFU 缓存。
LFU 缓存
一个经典的 LFU 缓存中,有两个散列表,一个是键值散列表,里面存放的是节点。还有一个是频率散列表,里面根据每个访问频率,会有一个双链表,如下图中所示,如果键值为 2 和 3 的两个节点内容都是被访问一次,那么他们在频率 1 下面会按照访问顺序被加到链表中。
Day17 如何通过链表做LRU/LFU缓存?_第1张图片
一个 LFU 缓存里面有两个散列表,一个是键值散列表,一个是频率散列表。
插入新的节点:这里我们要看缓存是否已经满了,如果是,那么在插入时它的频率是 1。如果没有满的话,那么新的元素在插入的同时,尾部的元素就会被删除。
更新旧的节点:这时,这个元素会被移动到表头。同时,为了计算下一个被删除的元素,最小的频率 minFreq 会减 1。
获取节点的动作:缓存可以返回节点,并且增加它的调用频率的计数,同时将这个元素移动到双链表的头部。同样与插入场景类似,最后一步,最低频率 minFreq 的值会被调整,用来计算下次操作中会被取代的元素。

class LFUCache {
  constructor() {
    this.keys = {}; // 用来存储LFU节点的键值散列表
    this.freq = {}; // 用来存储LFU链表的频率散列表
    this.capacity = capacity; // 用来定义缓存的承载量
    this.minFreq = 0; // 把最低访问频率初始设置为0
    this.size = 0; // 把缓存大小初始设置为0
  }
  set() {/* 插入一个节点 */}
  get() {/* 读取一个节点 */}
}

LRU缓存
LRU 缓存的目的是在有限的内存空间中,当有新的内容需要缓存的时候,清除最老的内容缓存。当一个缓存中的元素被访问了,这个最新的元素就会被放到列表的最后。当一个没有在缓存中的内容被访问的时候,最前面的也就是最老的内容就会被删除。

class LRUNode extends DoublyNode { 
  constructor(key) {
    this.key = key; 
  }
}

class LRUCache {
  constructor() {
    this.keys = {}; // 用来存储LFU节点的键值散列表
    this.capacity = capacity; // 用来定义缓存的承载量
    this.size = 0; // 把缓存大小初始设置为0
  }
  set() {/* 插入一个节点 */}
  get() {/* 读取一个节点 */}
}

你可能感兴趣的:(前端javascript)