LRU 缓存算法实现:使用 TypeScript
在计算机科学中,缓存是一种用于临时存储数据的技术,以便在需要时可以更快地访问数据。其中一种常见的缓存策略是 最近最少使用 (LRU) 算法。本文将介绍 LRU 缓存算法的基本概念,并使用 TypeScript 语言实现一个简单的 LRU 缓存。
LRU 缓存算法简介
LRU 算法的核心思想是:当缓存达到最大容量时,最近最少使用的项将被移除以便为新项腾出空间。为了实现这一策略,我们需要一个数据结构来跟踪缓存中的数据访问顺序。
数据结构
为了实现 LRU 缓存,我们将使用以下两种数据结构:
- 双向链表:用于存储缓存项并按访问顺序排列。
- 哈希表:用于在 O(1) 时间内查找缓存项。
双向链表节点定义
首先,我们需要定义双向链表节点,它将包含键、值和指向前后节点的指针。
class ListNode {
key: number;
value: number;
prev: ListNode | null;
next: ListNode | null;
constructor(key: number, value: number) {
this.key = key;
this.value = value;
this.prev = null;
this.next = null;
}
}
LRU 缓存实现
接下来,我们实现 LRU 缓存类,它将包含以下方法:
constructor(capacity: number):初始化缓存的容量。get(key: number): 获取给定键的值,如果键不存在,则返回 -1。put(key: number, value: number): 更新给定键的值,如果键不存在,则插入一个新的键值对。如果缓存已满,需要移除最近最少使用的项。
class LRUCache {
capacity: number;
size: number;
cache: Map<number, ListNode>;
head: ListNode;
tail: ListNode;
constructor(capacity: number) {
this.capacity = capacity;
this.size = 0;
this.cache = new Map();
this.head = new ListNode(-1, -1); // dummy head node
this.tail = new ListNode(-1, -1); // dummy tail node
this.head.next = this.tail;
this.tail.prev = this.head;
}
get(key: number): number {
if (this.cache.has(key)) {
const node = this.cache.get(key)!;
this.moveToFront(node);
return node.value;
}
return -1;
}
put(key: number, value: number): void {
if (this.cache.has(key)) {
const node = this.cache.get(key)!;
node.value = value;
this.moveToFront(node);
} else {
const newNode = new ListNode(key, value);
this.cache.set(key, newNode);
this.insertToFront(newNode);
this.size++;
if (this.size > this.capacity) {
const lastNode = this.removeLast();
this.cache.delete(lastNode.key);
this.size--;
}
}
}
// Helper methods for operating on the doubly linked list
private moveToFront(node: ListNode): void {
this.removeNode(node);
this.insertToFront(node);
}
private removeNode(node: ListNode): void {
node.prev!.next = node.next;
node.next!.prev = node.prev;
}
private insertToFront(node: ListNode): void {
node.next = this.head.next;
node.prev = this.head;
this.head.next!.prev = node;
this.head.next = node;
}
private removeLast(): ListNode {
const lastNode = this.tail.prev!;
this.removeNode(lastNode);
return lastNode;
}
}
示例
以下是使用我们的 LRU 缓存实现的几个示例:
示例 1:
const lruCache = new LRUCache(2);
lruCache.put(1, 1);
lruCache.put(2, 2);
console.log(lruCache.get(1)); // 输出 1
lruCache.put(3, 3);
console.log(lruCache.get(2)); // 输出 -1
lruCache.put(4, 4);
console.log(lruCache.get(1)); // 输出 -1
console.log(lruCache.get(3)); // 输出 3
console.log(lruCache.get(4)); // 输出 4
在这个示例中,我们创建了一个容量为 2 的 LRU 缓存。我们首先插入键值对 (1, 1) 和 (2, 2),然后使用 get 方法获取键 1 的值,它存在于缓存中,因此返回 1。接下来,我们插入键值对 (3, 3),这将导致键 2 被移除,因为它是最近最少使用的项。然后,我们插入键值对 (4, 4),这将导致键 1 被移除,因为它是最近最少使用的项。最后,我们使用 get 方法获取键 1、3 和 4 的值,它们在缓存中分别对应值 -1、3 和 4。