题目:要求实现一个LRU算法。
分析:
1 容量大小有限位N
2 get/set某个值都会变为最新
3 容量满了需要淘汰最不常用的值
4 get/set 在O(1)时间复杂度获取
方案:
考虑用队列实现
设计:
双向链表表示队列,首部存放最新元素,尾部存放最久未使用的元素。
Hash结构实现O(1)查找链表的能力,参数key作为hash的key,值存放链表中的节点。
实现思路:
get(key)
1 若key不存在,返回-1。
2 若key存在,则使用hash快速定位该值,然后把该值放到链表首部。
set(key,value)
1 如果key存在,则hash找到该值,然后把该值放到链表首部,并更新该值,更新hash。
2 如果key不存在,若容量未满,创建一个新节点,把该节点放到链表首部,hash表增加这个节点。
3 如果key不存在,若容量已满,删除链表最后元素,创建一个新节点,把该节点放到链表首部,hash增加这个节点,删除hash旧节点。
代码实现:
这里使用双链表为了减少判定,在使用链表增加/删除第一个或最后一个元素时,链表结尾始终不为空,使用的是带头尾节点的双链表,因此LRU结构体包含head和tail指针。
package main
import "fmt"
// LRU结构
type LRU struct {
// 容量
cap int
// 队列长度
len int
// 链表头
head *Node
// 链表尾
tail *Node
// hash表
hash map[int]*Node
}
// Node节点
type Node struct {
// key键
key int
// value值
value int
// 双链表下一个节点
next *Node
// 双链表上一个节点
prev *Node
}
// 实例化一个LRU
func NewLRU(cap int) *LRU {
h := newNode(0, 0)
t := newNode(0, 0)
h.next = t
t.prev = h
return &LRU{
cap: cap,
head: h,
tail: t,
hash: make(map[int]*Node, 0),
}
}
// 获取key对外接口
func (l *LRU) Get(key int) int {
if v, ok := l.hash[key]; !ok {
return -1
} else {
l.moveToHead(v)
return v.value
}
}
// 移动到链表头
func (l *LRU) moveToHead(n *Node) {
n.prev.next = n.next
n.next.prev = n.prev
l.appendToHead(n)
}
// 创建节点
func newNode(key int, value int) *Node {
return &Node{
key: key,
value: value,
}
}
// 插入到链表头
func (l *LRU) appendToHead(n *Node) {
n.prev = l.head
n.next = l.head.next
l.head.next = n
n.next.prev = n
}
// 删除链表尾部元素
func (l *LRU) deleteTail() *Node {
v := l.tail.prev
l.tail.prev.prev.next = l.tail
l.tail.prev = l.tail.prev.prev
return v
}
// 对外key/value设置接口
func (l *LRU) Set(key int, value int) {
// 如果key存在,则hash找到该值,然后把该值放到链表首部,并更新该值,更新hash
if v, ok := l.hash[key]; ok {
v.value = value
l.moveToHead(v)
} else {
// 如果key不存在,若容量未满,创建一个新节点把该节点放到链表首部,hash增加这个节点
n := newNode(key, value)
if l.cap > l.len {
l.len++
} else {
//如果key不存在,若容量满了,删除链表最后元素,创建一个新节点把该节点放到链表首部,hash增加这个节点
v := l.deleteTail()
delete(l.hash, v.key)
}
l.appendToHead(n)
l.hash[key] = n
}
}
// 显示打印
func (l *LRU) Show() {
h := l.head
for h.next != nil && h.next != l.tail {
h = h.next
fmt.Println(h.key, h.value)
}
}
func main() {
l := NewLRU(5)
l.Set(1, 3)
l.Set(2, 3)
l.Set(2, 3)
l.Get(1)
l.Set(5, 3)
l.Set(6, 3)
l.Get(2)
l.Set(7, 3)
l.Get(1)
l.Set(8, 3)
fmt.Println(l.Get(5))
l.Show()
}
结果:
-1
8 3
1 3
7 3
2 3
6 3