【发布时间】:2021-11-01 11:42:36
【问题描述】:
我在 LeetCode 上使用常规方法(双链表+哈希图)实现了 LRU 缓存问题的设计。对于那些不熟悉这个问题的人,这个实现看起来像这样:
我明白为什么要使用这种方法(两端快速移除/插入,中间快速访问)。我不明白的是,当人们可以简单地使用基于数组的双端队列(在 Java ArrayDeque 中,C++ 只是双端队列)时,为什么有人会同时使用 HashMap 和 LinkedList。这个双端队列允许在两端轻松插入/删除,并在中间快速访问,这正是 LRU 缓存所需要的。您还可以使用更少的空间,因为您不需要存储指向每个节点的指针。
为什么 LRU 缓存几乎被普遍设计(至少在大多数教程中)使用后一种方法而不是 Deque/ArrayDeque 方法? HashMap/LinkedList 方法有什么好处吗?
【问题讨论】:
-
双向链表除了
linked list和doubly linked list之外,还可以同时是stack、queue、deque,非常方便。例如Java的LinkedList就是这样一个实现。实际上,当您尝试实现stack和queue时,您会发现大多数操作都可以通过简单地包装来自一个良好实现的双向链表的操作来完成。 -
[续] 如果大小经常变化,那么基于数组的结构可能需要经常调整大小,这很昂贵,因此对性能不利。
-
不使用基于数组的结构的另一个主要原因是从非端添加/删除成本高昂,需要移动以下元素,并且可能导致调整大小。虽然链表没有这个问题,但链表的问题是它不能快速访问中间的元素,但如果你只访问2端,那么双向链表也是O(1)。
-
@EricWang 您只能从 LRU 缓存的开头和结尾删除。
-
如果您已经编写了 LRU 缓存并且不需要
move_to_end操作,那么您可能已经编写了 FIFO 缓存并且不是 LRU 缓存。