写给你的骚操作:实现一下LRU算法

                       筚路蓝缕,以启山林。不鸣则已,一鸣惊人。

                                                                                                          ——《左转`宣公十二年》

目录

LRU

总结与设计

实现LRU

验证下我的骚操作


双向链表实现可移步:双向链表

LRU

LRU(Least Recently Used):是一种按访问时序的缓存淘汰策略:最近使用过的数据是有用的,很久没用的数据是无用的,内存满了时优先删除最久没用的那个数据。

情景再现:

举例说明下。假如你的手机运存所支持的当前缓存的最大后台程序数为4,你由久及近依次打开了计算器、微信、手电筒、日历四个程序,即最新打开的是日历,最久打开的是计算器。

操作1:你现在重新点开了“微信”,这时候你再查看后台当前运行的程序列表,由久及近应该会变为计算器、手电筒、日历、微信,即最新打开的“微信”程序被排在了开头, 其他的依次后退;

操作2:你现在打开了一个新程序“时钟”,那么后台程序列表将变成:手电筒、日历、微信、时钟 ,按照LRU策略,因为内存满了,最久没有使用的是计算器,系统会自动把计算器删除出去,时钟是新打开的所以排在了最开头,刚刚排在开头的日历排到了第二位。

操作3:我主动划走了(删除了)后台的日历程序,那么由久及近将变成:手电筒、微信、时钟

总结与设计

特点如下:

1,获取其中某个数据,该数据被提到开头,其他的继续按原顺序接上

2,这组数据有顺序,删除其中一个其他的继续按原顺序接上

3,这组数据如果满了(达到了存储它的最大长度),此时再往里添加一个数据,那么尾部最后一个数据(最久未使用的)将被删除,新添加的将放在首部

删除元素节点操作可以用链表,链表的删除和插入操作快并且是有顺序的;哈希表查找(访问)某个元素快,因此可将二者结合取长补短,形成哈希链表。 因此设计如下:

链表表首<-->[k1:v1]<-->[k2:v2]<-->链表表尾

哈希表存储结构:基于map,与链表节点映射。

操作:如删除k2,链表将变为链表表首<-->[k1:v1]<-->链表表尾,同时哈希表中也将只剩k1一个键。

实现LRU

// 链表操作接口
type ListInterface interface {
	Add2Front(string)        // 链表表首添加新元素
	Remove(string)           // 删除指定元素
	RemoveLast(string) *Node // 删除最后一个元素
	Len(List) int            // 链表长度
}

const Max = 5                //定义缓存的最大容量
var hashMap map[string]*Node // 定义哈希表
var cache *List              // 定义缓存

type Node struct {
	Pre  *Node
	Next *Node
	Data DataStruct
}

type DataStruct struct {
	Key   string
	Value string
}

type List struct {
	First *Node
	Last  *Node
	Size  int
}

// 删除链表尾节点
func (list *List) RemoveLast() *Node {
	node := list.Last.Pre
	lastNode := list.Last
	node.Next = nil
	list.Last = node
	list.Size -= 1
	return lastNode
}

// 删除指定节点
func (list *List) Remove(value string) {
	current := list.First
	for current != nil {
		if current.Data.Key == value {
			pre := current.Pre
			next := current.Next
			pre.Next = next
			next.Pre = pre
		}
		current = current.Next
	}
	list.Size -= 1
}

// 添加新的首节点
func (list *List) Add2Front(key, value string) {
	newNode := new(Node)
	newNode.Data.Key = key
	newNode.Data.Value = value

	// 链表为空时
	if list.Size < 1 {
		list.First = newNode
		list.Last = newNode
	} else {
		// 链表的长度>=1时
		firstNode := list.First
		firstNode.Pre = newNode
		newNode.Next = firstNode
		list.First = newNode
	}

	list.Size += 1
}

func (list *List) Len() int {
	return list.Size
}

// 创建一个空的双链表
func CreateNewAirList() (list *List) {
	return &List{}
}

// 访问/查找某个数据
func (cache *List) Get(key string) *Node {
	if value, ok := hashMap[key]; ok { // 如果缓存中有
		cache.Put(key, value.Data.Value) // 添加到表首
		return value                     // 返回数据
	}

	return nil
}

// 添加数据/将数据提到开头
func (cache *List) Put(key, value string) {
	node := new(Node)
	node.Data.Key = key
	node.Data.Value = value
	if _, ok := hashMap[key]; ok { // 先检查,如果缓存中有
		cache.Remove(key)           // 删除旧数据
		cache.Add2Front(key, value) // 添加到表首
		hashMap[key] = node         // 更新map中的值
	} else {
		if cache.Size == Max { // 添加时容量已满
			cache.RemoveLast()
			delete(hashMap, key)
		}
		cache.Add2Front(key, value) // 添加到表首
		hashMap[key] = node         //更新map中的值
	}
}

// 删除某元素。要删除的后台程序必然是存在的
func (cache *List) RemoveApp(key string) {
	cache.Remove(key)
	delete(hashMap, key)
}

// 查看当前运行的后台程序列表
func (list *List) printNode() {
	head := list.First
	fmt.Print("\t\t")
	for head != nil {
		fmt.Print(head.Data, "\t")
		head = head.Next
	}
}

验证下我的骚操作

func main() {
	cache = CreateNewAirList()
	hashMap = make(map[string]*Node, 5)
	/* 测试新增 */
	cache.Put("日历", "日历value")
	cache.Put("计算器", "计算器value")
	cache.Put("微信", "微信value")
	cache.Put("时钟", "时钟value") // 最新打开的程序在表头
	fmt.Println("新开了四个程序,由近及久依次是:")
	cache.printNode()

	/* 测试访问 */
	fmt.Println("\n微信已在缓存中,再次访问微信,后台程序由近及久依次是:")
	cache.Get("微信")   // 访问微信
	cache.printNode() // 检查微信是否成了表头第一个程序,其他的顺序是否正确
	fmt.Println("\n新开了个手电筒程序:")
	cache.Put("手电筒", "手电筒value")
	cache.printNode()

	/* 测试新增让缓存占满 */
	fmt.Println("当前缓存容量已满:", cache.Len(), "  \n新开了个程序陌陌,后台程序由近及久依次是:")
	cache.Put("陌陌", "陌陌value")
	cache.printNode() //检查陌陌是不是在开头,表尾原来的日历是否没有了,其他几个的顺序是否正常的

	/* 测试删除缓存中某程序:划走(删除)手电筒 */
	fmt.Println("\n划走(删除)手电筒程序后:")
	cache.RemoveApp("手电筒")
	cache.printNode()
	fmt.Println("\n划走(删除)时钟程序后:")
	cache.RemoveApp("时钟")
	cache.printNode()
}

控制台

新开了四个程序,由近及久依次是:
	{时钟 时钟value}	{微信 微信value}	{计算器 计算器value}	{日历 日历value}	
微信已在缓存中,再次访问微信,后台程序由近及久依次是:
	{微信 微信value}	{时钟 时钟value}	{计算器 计算器value}	{日历 日历value}	
新开了个手电筒程序:
	{手电筒 手电筒value}	{微信 微信value}	{时钟 时钟value}	{计算器 计算器value}	{日历 日历value}	当前缓存容量已满: 5   
新开了个程序陌陌,后台程序由近及久依次是:
	{陌陌 陌陌value}	{手电筒 手电筒value}	{微信 微信value}	{时钟 时钟value}	{计算器 计算器value}	
划走(删除)手电筒程序后:
	{陌陌 陌陌value}	{微信 微信value}	{时钟 时钟value}	{计算器 计算器value}	
划走(删除)时钟程序后:
	{陌陌 陌陌value}	{微信 微信value}	{计算器 计算器value}	
Process finished with exit code 0
发布了189 篇原创文章 · 获赞 144 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/HYZX_9987/article/details/105205482
今日推荐