寫給你的騷操作:實現一下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

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章