📅  最后修改于: 2023-12-03 15:32:45.537000             🧑  作者: Mango
LRU,Least Recently Used,最近最少使用算法,是一种用于缓存管理的算法。在每个缓存块上分配一个最近使用的时间戳,每次访问缓存块时,更新时间戳,当缓存块块已满时,将最久未使用的块替换掉。LRU 完整表格是指利用哈希表和双向链表实现的 LRU 算法,用于解决面试中的 LRU 缓存问题。
在实现 LRU 完整表格前,需要先了解哈希表和双向链表的概念。
哈希表,也被称为散列表,是根据关键码值(Key-Value)而直接进行访问的数据结构。
哈希表使用哈希函数将关键码值映射到表中的位置,以加快查找的速度。
哈希函数的设计非常重要,一个好的哈希函数应该将不同的关键码值映射到不同的位置,减少哈希冲突的可能性。
下面是一个简单的哈希表的实现:
class HashTable {
constructor() {
this.table = new Array(10);
}
put(key, value) {
const hash = this._hash(key);
if (!this.table[hash]) {
this.table[hash] = {};
}
this.table[hash][key] = value;
}
get(key) {
const hash = this._hash(key);
if (this.table[hash] && this.table[hash][key]) {
return this.table[hash][key];
} else {
return null;
}
}
remove(key) {
const hash = this._hash(key);
if (this.table[hash] && this.table[hash][key]) {
delete this.table[hash][key];
}
}
_hash(key) {
let sum = 0;
for (let i = 0; i < key.length; i++) {
sum += key.charCodeAt(i);
}
return sum % this.table.length;
}
}
双向链表是一种链式存储结构,它除了数据元素以外,还有两个指针 prev 和 next,分别指向前一个节点和后一个节点。
双向链表可以实现很多操作,比如在任意一个节点后插入、删除一个节点等。这些操作都可以在常数时间内完成,因为不需要像数组一样,遍历整个数组来查找元素。
下面是一个简单的双向链表的实现:
class Node {
constructor(value) {
this.value = value;
this.prev = null;
this.next = null;
}
}
class DoublyLinkedList {
constructor() {
this.head = null;
this.tail = null;
this.size = 0;
}
append(value) {
const node = new Node(value);
if (!this.head) {
this.head = node;
this.tail = node;
} else {
node.prev = this.tail;
this.tail.next = node;
this.tail = node;
}
this.size++;
}
remove(node) {
if (node.prev) {
node.prev.next = node.next;
} else {
this.head = node.next;
}
if (node.next) {
node.next.prev = node.prev;
} else {
this.tail = node.prev;
}
this.size--;
}
removeFirst() {
if (!this.head) {
return null;
}
const node = this.head;
if (this.head === this.tail) {
this.head = null;
this.tail = null;
} else {
this.head = this.head.next;
this.head.prev = null;
}
this.size--;
return node;
}
}
有了哈希表和双向链表的基础,就可以实现 LRU 完整表格了。
首先,定义一个类 LRUCache,它有两个属性:capacity 和 map。capacity 表示缓存块的最大容量,map 是一个哈希表,用于将 key 映射到双向链表中的节点。
LRUCache 有三个方法:get、put 和 _refresh。
下面是 LRU 完整表格的实现:
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.map = new HashTable();
this.list = new DoublyLinkedList();
}
get(key) {
const node = this.map.get(key);
if (node) {
this.list.remove(node);
this.list.append(node.value);
return node.value.value;
} else {
return -1;
}
}
put(key, value) {
if (this.map.get(key)) {
const node = this.map.get(key);
node.value.value = value;
this.list.remove(node);
this.list.append(node.value);
} else {
if (this.list.size === this.capacity) {
const node = this.list.removeFirst();
this.map.remove(node.value.key);
}
const newNode = new Node({ key, value });
this.list.append(newNode);
this.map.put(key, newNode);
}
}
_refresh(node) {
this.list.remove(node);
this.list.append(node.value);
}
}
LRU 完整表格是一种用于解决缓存问题的算法。它利用哈希表和双向链表实现,可以在常数时间内完成插入、查询、删除等操作。
熟悉哈希表和双向链表的实现过程,可以帮助我们更好地理解 LRU 的实现过程。这些数据结构是非常常用的,需要程序员熟练掌握。