时间复杂度:O(1)
解题思路
看到要求时间复杂度为O(1)就应该能马上想到用哈希表,通过key迅速找到对应的结点。又因为我们需要经常修改顺序,使用链表就是一个很好的选择,新的缓存放在头结点,旧的缓存放在后面,缓存超过容量时就删除尾结点的缓存。
但是由于普通单向链表在删除尾结点时需要遍历整个链表,我们就可以用双向链表来提高效率,用O(1)的时间复杂度删除尾结点。
哈希表中key是给定的key,value是结点指针,方便我们在删除结点时快速找到位置。
在实现过程中,建议对双向链表增加虚拟头结点和虚拟尾结点,这样在添加删除时写代码更方便一些,少了很多的判断。
AC代码
- 声明LRUCache和Node两个结构体,然后声明Constructor、Get、Put、Add和Remove五个方法
- 实现LRUCache,成员包括cap、mp、head和tail
- 实现Node,成员包括key、value、pre和next
- 实现Constructor,根据LRUCache成员创建初始变量,即创建虚拟头尾结点、给map分配空间和返回一个LRUCache
- 实现Add,功能是插入结点到双向链表头部
- 实现Remove,功能是从双向链表中删除指定的结点
- 实现Get,判断是否命中,命中的话Remove结点、Add结点和返回value
- 实现Put,先判断是否命中,命中的话模仿Get添加修改value的过程;未命中就新创建一个结点,指定key和value值、map添加键值对、Add添加新结点。最后判断map的长度是否大于cap,若大于删除双向链表尾结点。
type LRUCache struct{
cap int
head,tail *Node
mp map[int]*Node
}
type Node struct{
key,value int
pre,next *Node
}
func Constructor(capacity int)LRUCache{
head,tail:=&Node{-1,-1,nil,nil},&Node{-1,-1,nil,nil}
head.next=tail
tail.pre=head
mp:=make(map[int]*Node,capacity)
return LRUCache{capacity,head,tail,mp}
}
func (this *LRUCache)Get(key int)int{
if node,ok:=this.mp[key];ok{
Remove(node)
this.Add(node)
return node.value
}else{
return -1
}
}
func (this *LRUCache)Put(key,value int){
if node,ok:=this.mp[key];ok{
node.value=value
Remove(node)
this.Add(node)
}else{
newNode:=&Node{key:key,value:value}
this.mp[key]=newNode
this.Add(newNode)
}
if len(this.mp)>this.cap{
delete(this.mp,this.tail.pre.key)
Remove(this.tail.pre)
}
}
func (this *LRUCache)Add(node *Node){
node.next=this.head.next
node.pre=this.head
this.head.next=node
node.next.pre=node
}
func Remove(node *Node){
node.pre.next=node.next
node.next.pre=node.pre
}
/**
* Your LRUCache object will be instantiated and called as such:
* obj := Constructor(capacity);
* param_1 := obj.Get(key);
* obj.Put(key,value);
*/
感悟
一开始命名结点名是ListNode,一运行就提示编译错误未定义序列化和反序列化。debug了一小时,挨个地方改,就是报错,不知道到底是力扣的原因还是go的原因。
先Remove再Add