leetcode 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) 的平均时间复杂度运行;
示例

输入:
[“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出:
[null, null, null, 1, null, -1, null, -1, 3, 4]

解析

这道题在面试中被问到的概率较大,因为既考察了链表,又考察了哈希表,又有一定的综合能力,比较容易扩展。
在本题中,实现LRU缓存是用哈希表+双向链表来实现。注意双向链表一般会加上头尾的伪结点来方便定位。

type LRUCache struct {
	size     int
	capacity int
	cache    map[int]*DlinkedNode
	head     *DlinkedNode
	tail     *DlinkedNode
}

type DlinkedNode struct {
	key   int
	value int
	prev  *DlinkedNode
	next  *DlinkedNode
}

func initDlinkedNode(key, value int) *DlinkedNode {
	return &DlinkedNode{
		key:   key,
		value: value,
	}
}

func Constructor(capacity int) LRUCache {
	l := LRUCache{
		capacity: capacity,
		cache:    map[int]*DlinkedNode{},
		head:     initDlinkedNode(0, 0),
		tail:     initDlinkedNode(0, 0),
	}
	l.head.next = l.tail
	l.tail.prev = l.head
	return l
}

func (this *LRUCache) Get(key int) int {
	if node, ok := this.cache[key]; !ok {
		return -1
	} else {
		this.moveToHead(node) // 第一个自定义函数
		return node.value
	}
}

func (this *LRUCache) Put(key int, value int) {
	if node, ok := this.cache[key]; !ok {
		newNode := initDlinkedNode(key, value)
		this.cache[key] = newNode
		this.addToHead(newNode) // 第二个自定义函数
		this.size++
		if this.size > this.capacity {
			removed := this.removeTail() // 第三个自定义函数
			delete(this.cache, removed.key)
			this.size--
		}
	} else {
		node.value = value
		this.moveToHead(node)
	}
}

func (this *LRUCache) moveToHead(node *DlinkedNode) {
	this.removeNode(node)
	this.addToHead(node)
}

func (this *LRUCache) removeNode(node *DlinkedNode) {
	node.next.prev = node.prev
	node.prev.next = node.next
}
// 主要是这个方法不好写,同时下面的顺序不能变
// 顺序为:node前、node后
// 头节点下一个前、头节点后
// 即:插入的前后、原来的前后
func (this *LRUCache) addToHead(node *DlinkedNode) {
	node.prev = this.head
	node.next = this.head.next
	this.head.next.prev = node
	this.head.next = node
}

func (this *LRUCache) removeTail() *DlinkedNode {
	node := this.tail.prev
	this.removeNode(node)
	return node
}

上面的代码是自己实现了双向链表,其实go中已经有list包了,可以简化不少逻辑:

type LRUCache struct {
	capacity  int
	list      *list.List
	keyToNode map[int]*list.Element
}

type entry struct {
	key   int
	value int
}

func Constructor(capacity int) LRUCache {
	return LRUCache{
		capacity:  capacity,
		list:      list.New(),
		keyToNode: map[int]*list.Element{},
	}
}

func (c *LRUCache) Get(key int) int {
	node := c.keyToNode[key]
	if node == nil {
		return -1
	}
	c.list.MoveToFront(node)
	return node.Value.(entry).value
}

func (c *LRUCache) Put(key, value int) {
	if node, ok := c.keyToNode[key]; ok {
		node.Value = entry{key: key, value: value}
		c.list.MoveToFront(node)
		return
	}
	c.keyToNode[key] = c.list.PushFront(entry{key: key, value: value}) // 新的放在最上面
	if len(c.keyToNode) > c.capacity {
		delete(c.keyToNode, c.list.Remove(c.list.Back()).(entry).key)
	}
}

注意上面的代码中,结构体entry的作用,其实在前面的代码中,确实也用不到,都直接用value就行,但是在超过cap需要删除的时候,就需要拿key来删除了,所以就把key value都保存起来,反正list的那个节点存的是any,取出来的时候断言成结构体就行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值