LRU的实现

3 篇文章 0 订阅
这篇博客详细介绍了LRU(最近最少使用)算法的原理及其在缓存管理中的应用。通过使用双向链表和哈希表,实现了在O(1)时间复杂度内进行get和set操作,并确保当缓存满时能有效淘汰最不常用的元素。代码示例展示了如何用Go语言实现LRU缓存,包括节点的添加、移动到头部、删除等操作。
摘要由CSDN通过智能技术生成

力扣

题目:要求实现一个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 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值