本题是数据结构设计题。
为实现在时间复杂度内完成put和get两种操作,LRUCache基于一个哈希表(map)和一个双向链表来实现。哈希表的插入、查询和删除的时间复杂度都是,双向链表的插入、删除操作的时间复杂度都是,它们共同作用使得get和put操作的时间复杂度是。请看代码细节。
具体解答如下。牛客网题目的解法在此基础上稍作修改即可。
基于自定义的双向链表(已提交通过)
type LRUCache struct {
m map[int]*Item
list *DoublyLinkedList
}
func Constructor(capacity int) LRUCache {
if capacity < 1 {
panic("capacity < 1")
}
return LRUCache{
m: make(map[int]*Item, capacity),
list: NewDoublyLinkedList(capacity),
}
}
func (this *LRUCache) Get(key int) int {
item, ok := this.m[key]
if !ok {
return -1
}
this.list.Move2Head(item) // 设置为最“热”数据
return item.Val
}
func (this *LRUCache) Put(key int, value int) {
item, ok := this.m[key]
if ok { // 已存在
item.Val = value // 更新value
this.list.Move2Head(item) // 设置为“最热”数据
} else {
// 插入新数据
item, removed := this.list.AddToHead(key, value)
this.m[key] = item
if removed != nil {
// 有链表节点被删除了,要同步删除map中的数据
delete(this.m, removed.Key)
}
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* obj := Constructor(capacity);
* param_1 := obj.Get(key);
* obj.Put(key,value);
*/
// 双向链表节点
type Item struct {
Key int // 关键字
Val int // 值
Prev *Item // 指向直接前驱
Next *Item // 指向直接后继
}
// 双向链表
type DoublyLinkedList struct {
head *Item // 头节点
tail *Item // 尾节点
size int // 当前节点个数
capacity int // 最大节点个数限制
}
func NewDoublyLinkedList(capacity int) *DoublyLinkedList {
return &DoublyLinkedList{
capacity: capacity,
}
}
func (o *DoublyLinkedList) AddToHead(key int, val int) (item, removed *Item) {
item = &Item{
Key: key,
Val: val,
}
if o.head != nil {
item.Next = o.head
o.head.Prev = item
o.head = item
} else {
o.head = item
o.tail = item
}
o.size++
if o.size > o.capacity {
removed = o.removeTail()
}
return
}
func (o *DoublyLinkedList) removeTail() *Item {
// 此处o.head和o.tail肯定不是nil
tail := o.tail
if o.head != o.tail {
prev := o.tail.Prev // 此处prev肯定不是nil
prev.Next = nil // 注意置nil
o.tail = prev
} else {
o.head = nil
o.tail = nil
}
o.size--
return tail
}
func (o *DoublyLinkedList) Move2Head(item *Item) {
if item == o.head {
return
}
// 此处item.Prev肯定不是nil
item.Prev.Next = item.Next
if item != o.tail {
item.Next.Prev = item.Prev // 此处item.Next肯定不是nil
} else {
o.tail = item.Prev
}
item.Prev = nil // 注意置nil
item.Next = o.head
o.head.Prev = item
o.head = item
}
基于container/list(已提交通过)
import "container/list"
type LRUCache struct {
m map[int]*list.Element
list *list.List
capacity int
}
func Constructor(capacity int) LRUCache {
if capacity < 1 {
panic("capacity < 1")
}
return LRUCache{
m: make(map[int]*list.Element, capacity),
list: list.New(),
capacity: capacity,
}
}
func (this *LRUCache) Get(key int) int {
e, ok := this.m[key]
if !ok {
return -1
}
this.list.MoveToFront(e) // 设置为最“热”数据
return e.Value.(*Pair).Value
}
func (this *LRUCache) Put(key int, value int) {
e, ok := this.m[key]
if ok { // 已存在
e.Value.(*Pair).Value = value // 更新value
this.list.MoveToFront(e) // 设置为“最热”数据
} else {
if this.list.Len() == this.capacity {
e = this.list.Back()
delete(this.m, e.Value.(*Pair).Key)
this.list.Remove(e)
}
// 插入新数据
this.m[key] = this.list.PushFront(&Pair{
Key: key,
Value: value,
})
}
}
type Pair struct {
Key int
Value int
}
/**
* Your LRUCache object will be instantiated and called as such:
* obj := Constructor(capacity);
* param_1 := obj.Get(key);
* obj.Put(key,value);
*/