JavaScript手寫LRU算法的示例代碼
LRU 是 Least Recently Used 的縮寫,即最近最少使用。作為一種經(jīng)典的緩存策略,它的基本思想是長(zhǎng)期不被使用的數(shù)據(jù),在未來被用到的幾率也不大,所以當(dāng)新的數(shù)據(jù)進(jìn)來時(shí)我們可以優(yōu)先把這些數(shù)據(jù)替換掉。
一、基本要求
- 固定大小:限制內(nèi)存使用。
- 快速訪問:緩存插入和查找操作應(yīng)該很快,最好是 O(1) 時(shí)間。
- 在達(dá)到內(nèi)存限制的情況下替換條目:緩存應(yīng)該具有有效的算法來在內(nèi)存已滿時(shí)驅(qū)逐條目。
二、數(shù)據(jù)結(jié)構(gòu)
下面提供兩種實(shí)現(xiàn)方式,并完成相關(guān)代碼。
2.1 Map
在 Javascript 中,Map 的 key 是有序的,當(dāng)?shù)臅r(shí)候,他們以插入的順序返回鍵值。結(jié)合這個(gè)特性,我們也通過 Map 實(shí)現(xiàn) LRU 算法。
2.2 Doubly Linked List
我們也可通過雙向鏈表(Doubly Linked List)維護(hù)緩存條目,通過對(duì)鏈表的增、刪、改實(shí)現(xiàn)數(shù)據(jù)管理。為確保能夠從鏈表中快速讀取某個(gè)節(jié)點(diǎn)的數(shù)據(jù),我們可以通過 Map 來存儲(chǔ)對(duì)鏈表中節(jié)點(diǎn)的引用。
三、Map 實(shí)現(xiàn)
在 初始化時(shí) 完成兩件事情:
1.配置存儲(chǔ)限制,當(dāng)大于此限制,緩存條目將按照最近讀取情況被驅(qū)逐。
2.創(chuàng)建一個(gè)用于存儲(chǔ)緩存數(shù)據(jù)的 Map 。
在 添加數(shù)據(jù) 時(shí):
1.判斷當(dāng)前存儲(chǔ)數(shù)據(jù)中是否包含新進(jìn)數(shù)據(jù),如果存在,則刪除當(dāng)前數(shù)據(jù)
2.判斷當(dāng)前存儲(chǔ)空間是否被用盡,如果已用盡則刪除 Map 頭部的數(shù)據(jù)。
map.delete(map.keys().next().value)
3.插入新數(shù)據(jù)到 Map 的尾部
基于 Javascript Map 實(shí)現(xiàn) LRU,代碼如下:
class LRUCache {
size = 5
constructor(size) {
this.cache = new Map()
this.size = size || this.size
}
get(key) {
if (this.cache.has(key)) {
// 存在即更新
let temp = this.cache.get(key)
this.cache.delete(key)
this.cache.set(key, temp)
return temp
}
return null
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key)
}
if (this.cache.size >= this.size) {
this.cache.delete(this.cache.keys().next().value)
}
this.cache.set(key, value)
}
}
四、雙向鏈表實(shí)現(xiàn)
4.1 定義節(jié)點(diǎn)類
包含 prev,next,data 三個(gè)屬性,分別用以存儲(chǔ)指向前后節(jié)點(diǎn)的引用,以及當(dāng)前節(jié)點(diǎn)要存儲(chǔ)的數(shù)據(jù)。
{
prev: Node
next: Node
data: { key: string, data: any}
}
4.2 定義鏈表類
包含 head、tail 屬性,分別指向鏈表的 頭節(jié)點(diǎn) 和 尾節(jié)點(diǎn)。
當(dāng)從鏈表中讀取數(shù)據(jù)時(shí),需要將當(dāng)前讀取的數(shù)據(jù)移動(dòng)到鏈表頭部;添加數(shù)據(jù)時(shí),將新節(jié)點(diǎn)插入到頭部;當(dāng)鏈表節(jié)點(diǎn)數(shù)量大于限定的閥值,需要從鏈表尾部刪除節(jié)點(diǎn)。
{
head: Node
next: Node
moveNodeToHead(node)
insertNodeToHead(node)
deleteLastNode()
}
4.3 定義 LRU 類
為 LRU 定義屬性:linkLine 用以存儲(chǔ)指向鏈表的引用;size 用以配置存儲(chǔ)空間大小限制;
為簡(jiǎn)化從鏈表中查找節(jié)點(diǎn),再定義 map 屬性,用以存儲(chǔ)不同鍵指向鏈表節(jié)點(diǎn)的引用。
定義成員方法,set(key,value) 用以添加數(shù)據(jù),get(key) 讀取一條數(shù)據(jù)。
4.4 set(key,value)
如果 map 中存在當(dāng)前 key,則修改當(dāng)前節(jié)點(diǎn)的值,然后從鏈表中把當(dāng)前節(jié)點(diǎn)移動(dòng)到鏈表頭部;
否則:
判斷當(dāng)前鏈表節(jié)點(diǎn)數(shù)量是否達(dá)到了存儲(chǔ)上線,如果是,則刪除鏈表尾部的節(jié)點(diǎn)。同時(shí)從 map 中移除相應(yīng)的節(jié)點(diǎn)引用;
創(chuàng)建新節(jié)點(diǎn),然后插入到鏈表頭部,并添加 map 引用。
4.5 get(key)
如果 map 中存在當(dāng)前 key,從鏈表中讀取節(jié)點(diǎn),將其移動(dòng)到鏈表頭部,并返回結(jié)果,否則返回空。
{
linkLine: LinkLine
map: Map
size: Number
set(key, value)
get(key)
}
4.6 代碼實(shí)現(xiàn)
class LinkNode {
prev = null
next = null
constructor(key, value) {
this.data = { key, value }
}
}
class LinkLine {
head = null
tail = null
constructor() {
const headNode = new LinkNode('head', 'head')
const tailNode = new LinkNode('tail', 'tail')
headNode.next = tailNode
tailNode.prev = headNode
this.head = headNode
this.tail = tailNode
}
moveNodeToFirst(node) {
node.prev.next = node.next
node.next.prev = node.prev
this.insertNodeToFirst(node)
}
insertNodeToFirst(node) {
const second = this.head.next
this.head.next = node
node.prev = this.head
node.next = second
second.prev = node
}
delete(node) {
node.prev.next = node.next
node.next.prev = node.prev
}
deleteLastNode() {
const last = this.tail.prev
this.tail.prev = last.prev
last.prev.next = this.tail
return last
}
}
class LRUCache {
linkLine = null
map = {}
size = 5
constructor(size) {
this.size = size || this.size
this.linkLine = new LinkLine
}
get(key) {
let value
if (this.map[key]) {
const node = this.map[key]
value = node.value
this.linkLine.moveNodeToFirst(node)
}
return value
}
set(key, value) {
if (this.map[key]) {
const node = this.map[key]
node.value = value
this.linkLine.moveNodeToFirst(node)
} else {
// 刪除最后一個(gè)元素
if (Object.keys(this.map).length >= this.size) {
const lastNode = this.linkLine.deleteLastNode()
delete this.map[lastNode.data.key]
}
const newNode = new LinkNode(key, value)
this.linkLine.insertNodeToFirst(newNode)
this.map[key] = newNode
}
}
}到此這篇關(guān)于JavaScript手寫LRU算法的示例代碼的文章就介紹到這了,更多相關(guān)JavaScript LRU算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序分享卡片花樣玩法之私密消息和動(dòng)態(tài)消息
用戶可以發(fā)送小程序卡片給微信好友或者群,點(diǎn)擊小程序卡片可以直接進(jìn)入小程序,這篇文章主要給大家介紹了關(guān)于微信小程序分享卡片花樣玩法之私密消息和動(dòng)態(tài)消息的相關(guān)資料,需要的朋友可以參考下2023-11-11
bootstrap Table實(shí)現(xiàn)合并相同行
這篇文章主要為大家詳細(xì)介紹了bootstrapTable實(shí)現(xiàn)合并相同行,fastadmin框架同樣使用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07
JS簡(jiǎn)單生成兩個(gè)數(shù)字之間隨機(jī)數(shù)的方法
這篇文章主要介紹了JS簡(jiǎn)單生成兩個(gè)數(shù)字之間隨機(jī)數(shù)的方法,涉及javascript數(shù)值運(yùn)算的相關(guān)技巧,需要的朋友可以參考下2016-08-08
uni-file-picker文件選擇上傳功能實(shí)現(xiàn)
這篇文章主要介紹了uni-file-picker文件選擇上傳,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
JS或jQuery獲取ASP.NET服務(wù)器控件ID的方法
這篇文章主要介紹了JS或jQuery獲取ASP.NET服務(wù)器控件ID的方法,本文介紹一方法,解決如何使用js獲取ASP.NET控件在瀏覽器端生成html標(biāo)簽對(duì)應(yīng)的id,需要的朋友可以參考下2015-06-06
JavaScript中常見的數(shù)據(jù)格式化方式詳解
這篇文章主要為大家詳細(xì)介紹了JavaScript中常見的數(shù)據(jù)格式化方式,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解一下2023-12-12
用xhtml+css寫的相冊(cè)自適應(yīng) - 類似九宮格[兼容 ff ie6 ie7 opear ]
用xhtml+css寫的相冊(cè)自適應(yīng) - 類似九宮格[兼容 ff ie6 ie7 opear ]...2007-05-05
JavaScript仿商城實(shí)現(xiàn)圖片廣告輪播實(shí)例代碼
大家在逛購(gòu)物商城的時(shí)候不知道有沒有注意到商城首頁上面都會(huì)有各種輪播廣告,效果非常好,下面小編給大家整理特此分享給大家學(xué)習(xí)2016-02-02

