LRC缓存算法:
- 初次见到这个算法的时候,自己比较的懵,这是啥?为什么算法还有这个?慢慢看了看,算了,咱还是看看其他的算法。所以一直将这个LRU算法一直放在旁边。最近看了看redis和mysql,都会有这个算法的身影,突然感觉这个算法还不错。于是花了些时间研究了一些,其实吧!也没啥。看能看着有点点多。第一次吓住了,可是当我理解他的思路之后就感觉正常了能接受了。
上代码
- 可能乍一看,我去,代码这么多,我不看了。慢着!!!往下看,咱们分解讲一下。如果你还不理解,咱就是说:“这都是我的错,没有讲明白😄,但是咱们还是要好好的学习的,继续通过别的途径学习,加油⛽️”
package main
func main() {
}
//这里定义一个LRUCache的机构,后面主要操作的就是这里面的数据
type LRUCache struct {
size int //缓存中节点个数
capacity int //缓存中容量大小
cache map[int]*DLinkedNode //声明一个map类型的缓存,key为int类型,通过key获取对应的value(value:是具体的数据)
head, tail *DLinkedNode //声明两个节点(头节点,为节点以供操作)
}
//DLinkedNode 定义一个节点
type DLinkedNode struct {
key, value int //该节点中的key和value
prev, next *DLinkedNode //该节点指向上一个和下一个节点的指针
}
//initDLinkedNode 初始化一个节点
func initDLinkedNode(key, value int) *DLinkedNode {
//返回这个节点的key,value
return &DLinkedNode{
key: key,
value: value,
}
}
//Constructor 实现lru的构造函数
func Constructor(capacity int) LRUCache {
//实现LRU的构造函数,初始化一个可以使用的LRC
l := LRUCache{
cache: map[int]*DLinkedNode{},
head: initDLinkedNode(0, 0),
tail: initDLinkedNode(0, 0),
capacity: capacity,
}
//将头节点和尾节点的指针相互指向
l.head.next = l.tail
l.tail.prev = l.head
//将完整的LRC返回
return l
}
//获取节点数据
func (this *LRUCache) Get(key int) int {
//先查看当前的缓存中有没有key对应的缓存
//如果没有就返回:-1
if _, ok := this.cache[key]; !ok {
return -1
}
//如果有就返回对应的值,并将这个值移动到头部
node := this.cache[key]
this.moveToHead(node)
return node.value
}
//添加节点
func (this *LRUCache) Put(key int, value int) {
//先查看当前的缓存中有没有key对应的缓存
if _, ok := this.cache[key]; !ok {
//创建一个新的节点
node := initDLinkedNode(key, value)
//将节点数据加入缓存中
this.cache[key] = node
//将节点移到头部
this.addToHead(node)
//节点个数加一
this.size++
//判断当前缓存中节点的个数是否超过缓存的容量,如果超过就移除掉尾节点
if this.size > this.capacity {
removed := this.removeTail()
delete(this.cache, removed.key)
this.size-- //移除之后,对应的size减一
}
} else {
//如果有就更新这个key对应的value,然后将节点移动到头部
node := this.cache[key]
node.value = value
this.moveToHead(node)
}
}
//添加一个节点的时候将节点添加到头部
func (this *LRUCache) addToHead(node *DLinkedNode) {
//这是一个双向的节点,下面的操作实现向一个双向链表中插入一个节点,四个指向
//前两行,将插入的节点,prev指向head,next指向next
node.prev = this.head
node.next = this.head.next
//后两行,将head节点的next指向插入节点,将原来head指向的节点的prev指向当前节点
this.head.next.prev = node
this.head.next = node
}
//移除一个节点
func (this *LRUCache) removeNode(node *DLinkedNode) {
//移除一个节点,将这个节点的上一个节点的next指向当前节点的下一个节点,将下一个节点的prev指向当前节点的前一个节点
node.prev.next = node.next
node.next.prev = node.prev
}
//将一个节点移动到头部
func (this *LRUCache) moveToHead(node *DLinkedNode) {
//通过移除节点,然后将这个节点添加到头部的方式实现将一个节点移动到头部
this.removeNode(node)
this.addToHead(node)
}
//移除尾节点
func (this *LRUCache) removeTail() *DLinkedNode {
node := this.tail.prev
this.removeNode(node)
return node
}
具备知识:
这道题我的解题思路主要是围绕着链表进行的,所以可能有的小伙伴没有接触数据结构,然后看起来迷迷糊糊的,而且感觉代码量太多了,所以就不想学了。其实不然,越是这样的代码也是考验一个人的学习能力,大家可以尝试去理解,如果不能理解链表(没有系统学习过链表的同学可能对于prev,next,head,tail之间的逻辑比较的混乱),就去学习一下数据结构中的链表这一块。我相信学习之后的你在看这道题就是捋清楚逻辑的问题了
学无止境,加油⛽️