146. LRU 缓存
146. LRU 缓存 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
- LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
- int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
- void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
type LRUCache struct {
capacity, size int
nodes map[int]*DLinkedNode
head, tail *DLinkedNode
}
type DLinkedNode struct {
key, val int
prev, next *DLinkedNode
}
func Constructor(capacity int) LRUCache {
c := LRUCache{
capacity: capacity,
nodes: make(map[int]*DLinkedNode),
head: &DLinkedNode{},
tail: &DLinkedNode{},
}
c.head.next = c.tail
c.tail.prev = c.head
return c
}
func (c *LRUCache) Get(key int) int {
node, ok := c.nodes[key]
if !ok {
return -1
}
c.moveToHead(node)
return node.val
}
func (c *LRUCache) Put(key int, val int) {
old, ok := c.nodes[key]
if !ok {
node := &DLinkedNode{key: key, val: val}
c.nodes[key] = node
c.addToHead(node)
c.size++
if c.size > c.capacity {
tail := c.removeTail()
delete(c.nodes, tail.key)
c.size--
}
} else {
old.val = val
c.moveToHead(old)
}
}
func (c *LRUCache) removeNode(node *DLinkedNode) {
node.prev.next = node.next
node.next.prev = node.prev
}
func (c *LRUCache) removeTail() *DLinkedNode {
node := c.tail.prev
c.removeNode(node)
return node
}
func (c *LRUCache) moveToHead(node *DLinkedNode) {
c.removeNode(node)
c.addToHead(node)
}
func (c *LRUCache) addToHead(node *DLinkedNode) {
node.prev = c.head
node.next = c.head.next
c.head.next.prev = node
c.head.next = node
}
460. LFU 缓存
460. LFU 缓存 请你为 最不经常使用(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) 的平均时间复杂度运行。
type LFUCache struct {
keyToVal map[int]*Node
freqToNodes map[int]*DoubleList
capacity int
minFreq int
}
func Constructor(capacity int) LFUCache {
c := LFUCache{
capacity: capacity,
minFreq: 0,
keyToVal: make(map[int]*Node),
freqToNodes: make(map[int]*DoubleList),
}
return c
}
type Node struct {
key int
val int
prev *Node
next *Node
freq int
}
func (c *LFUCache) Get(key int) int {
node, ok := c.keyToVal[key]
if !ok {
return -1
}
c.increseFreq(node)
return node.val
}
func (c *LFUCache) Put(key int, value int) {
if c.capacity == 0 {
return
}
// 1. 存在则调整节点的频率即可
node, ok := c.keyToVal[key]
if ok {
node.val = value
c.increseFreq(node)
return
}
// 2. 判断capacity, 容量不足,删除最少访问的节点
if len(c.keyToVal) == c.capacity {
c.deleteMinFreqNodes()
}
// 3. 添加节点到双向链表和索引表
node = &Node{key: key, val: value, freq: 1}
c.keyToVal[key] = node
if c.freqToNodes[node.freq] == nil {
c.freqToNodes[node.freq] = NewDoubleList()
}
c.freqToNodes[node.freq].Add(node)
c.minFreq = 1
}
func (c *LFUCache) increseFreq(node *Node) {
originFreq := node.freq
node.freq++
// 1. 频率更新后,将节点从原来的频率链表中移除
dl := c.freqToNodes[originFreq]
// O(1)
dl.Remove(node)
// 2. 判断最低频率是否需要更新
if dl.IsEmpty() && originFreq == c.minFreq {
c.minFreq++
}
// 3. 将更新后的节点插入对应频率的链表的表头
if c.freqToNodes[node.freq] == nil {
c.freqToNodes[node.freq] = NewDoubleList()
}
c.freqToNodes[node.freq].Add(node)
}
func (c *LFUCache) deleteMinFreqNodes() {
// 1. 移除最少访问节点,双向链表按时间排序,新的在头,旧的在尾
dl := c.freqToNodes[c.minFreq]
lastn := dl.Last()
dl.Remove(lastn)
// 2. 移除索引
delete(c.keyToVal, lastn.key)
}
type DoubleList struct {
head, tail *Node
}
func NewDoubleList() *DoubleList {
head, tail := new(Node), new(Node)
head.next, tail.prev = tail, head
dl := &DoubleList{
head: head, tail: tail,
}
return dl
}
func (dl *DoubleList) Add(node *Node) {
prev, next := dl.head, dl.head.next
prev.next, node.prev = node, prev
next.prev, node.next = node, next
}
func (dl *DoubleList) Remove(node *Node) {
prev, next := node.prev, node.next
prev.next, next.prev = next, prev
node.prev, node.next = nil, nil
}
func (dl *DoubleList) First() *Node {
return dl.head.next
}
func (dl *DoubleList) Last() *Node {
return dl.tail.prev
}
func (dl *DoubleList) IsEmpty() bool {
return dl.head.next == dl.tail
}