请你为 最不经常使用(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) 的平均时间复杂度运行。
代码如下:
<?php
class Node
{
public $key;
public $val;
public $prev;
public $next;
public $count;
public function __construct($key, $val, $count = 1)
{
$this->key = $key;
$this->val = $val;
$this->count = $count;
}
}
class LinkDoubleList
{
protected $head;
protected $tail;
public function __construct()
{
$this->head = new Node(0, 0, PHP_INT_MAX);
$this->tail = new Node(-1, 0, -1);
$this->head->next = $this->tail;
$this->tail->prev = $this->head;
}
public function updateSort(Node $node)
{
$node->count++;
//取node前置节点
$prev = $node->prev;
while ($prev->count <= $node->count) {
//获取前置节点和后置节点
$start = $prev->prev;
$end = $node->next;
//开始互换位置
$start->next = $node;
$node->prev = $start;
$node->next = $prev;
$prev->prev = $node;
$prev->next = $end;
$end->prev = $prev;
//位置调换结束后,将node的前置节点更新
$prev = $start;
}
}
public function removeTail()
{
$last = $this->tail->prev;
$last->prev->next = $this->tail;
$this->tail->prev = $last->prev;
return $last->key;
}
public function addToLast(Node $node)
{
$prev = $this->tail->prev;
$prev->next = $node;
$node->prev = $prev;
$node->next = $this->tail;
$this->tail->prev = $node;
}
}
class LFUCache
{
public $linkDoubleList;
public $capacity;
public $cache = [];
public function __construct($capacity)
{
$this->linkDoubleList = new LinkDoubleList();
$this->capacity = $capacity;
}
public function get(int $key)
{
if (!isset($this->cache[$key])) {
return -1;
}
$node = $this->cache[$key];
$this->linkDoubleList->updateSort($node);
return $node->val;
}
public function put(int $key, int $value)
{
if (isset($this->cache[$key])) {
$this->cache[$key]->val = $value;
$this->linkDoubleList->updateSort($this->cache[$key]);
return;
}
if (count($this->cache) == $this->capacity) {
if ($this->capacity === 0) {
return;
}
$tail = $this->linkDoubleList->removeTail();
unset($this->cache[$tail]);
}
$node = new Node($key, $value);
$this->linkDoubleList->addToLast($node);
$this->linkDoubleList->updateSort($node);
$this->cache[$key] = $node;
}
}