LRU算法
题目要求:
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;
如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,
它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
要求:在O(1)时间复杂度内完成这两种操作
- 实现思路:因为涉及到
key,value键值对,所以可以想到利用哈希表进行存储,而题目要求在容量达到上限后写入新数据会删除最久未使用的数据,所以每次操作都应该更新数据的最近使用顺序,在数据结构中,可以快速进行删除添加操作的数据结构可以想到链表。所以可以通过链表存储各个数据的使用顺序,用哈希表存储key和其数据在链表中对应的node。在JS中,没有内置的链表,不过可以用Map的特性实现链表的功能。代码如下:
var LRUCache = function(capacity) {
this.map = new Map()
this.capacity = capacity
this.currentSiz = 0
};
LRUCache.prototype.get = function(key) {
if (this.map.has(key)) {
const res = this.map.get(key)
this.map.delete(key)
this.map.set(key, res)
return res
} else {
return -1
}
};
LRUCache.prototype.put = function(key, value) {
if (this.map.has(key)) {
this.map.delete(key)
this.map.set(key, value)
} else {
this.map.set(key, value)
if (this.currentSiz === this.capacity) {
const k = this.map.keys().next().value
this.map.delete(k)
} else {
this.currentSiz++
}
}
};
PS:不同于Object,Map中的数据是有序的,所以可以通过先删除,再增加来更新数据的最近使用,
而map.keys()返回的是具有iterator接口的类数组结构,可以通过.next().value方法获取值,
也可将其转化为数组后获取第一个值,这第一个值便是最久未使用过的数据。上述的操作都是通过
map实现的,所以时间复杂度为O(1)
在Vue中的使用
- 源码位置在
'src/core/components/keep-alive.js',部分源码如下
function pruneCache (keepAliveInstance: any, filter: Function) {
const { cache, keys, _vnode } = keepAliveInstance
for (const key in cache) {
const cachedNode: ?VNode = cache[key]
if (cachedNode) {
const name: ?string = getComponentName(cachedNode.componentOptions)
if (name && !filter(name)) {
pruneCacheEntry(cache, key, keys, _vnode)
}
}
}
}
function pruneCacheEntry (
cache: VNodeCache,
key: string,
keys: Array<string>,
current?: VNode
) {
const cached = cache[key]
if (cached && (!current || cached.tag !== current.tag)) {
cached.componentInstance.$destroy()
}
cache[key] = null
remove(keys, key)
}
export default {
name: 'keep-alive',
abstract: true,
props: {
include: patternTypes,
exclude: patternTypes,
max: [String, Number]
},
created () {
this.cache = Object.create(null)
this.keys = []
},
mounted () {
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
render () {
const slot = this.$slots.default
const vnode: VNode = getFirstComponentChild(slot)
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
const name: ?string = getComponentName(componentOptions)
const { include, exclude } = this
if (
(include && (!name || !matches(include, name))) ||
(exclude && name && matches(exclude, name))
) {
return vnode
}
const { cache, keys } = this
const key: ?string = vnode.key == null
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
remove(keys, key)
keys.push(key)
} else {
cache[key] = vnode
keys.push(key)
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
}
vnode.data.keepAlive = true
}
return vnode || (slot && slot[0])
}
}