問題インデックス(最大5):⭐⭐⭐⭐
タイトル
所有しているデータ構造を使用して、LRU(最近使用されていない)キャッシュメカニズムを設計および実装します。これは、次の機能をサポートする必要があります。データの取得get
および書き込みデータをput
。
データの取得get(key)
-キーがキャッシュに存在する場合は、キーの値を取得し(常に正の数)、それ以外の場合は-1を返します。
データの書き込みput(key, value)
-キーがすでに存在する場合は、そのデータ値を変更し、キーが存在しない場合は、グループ「キー/データ値」を挿入します。キャッシュ容量が上限に達すると、新しいデータを書き込む前に、最も長い未使用のデータ値を削除して、新しいデータ値のためのスペースを確保する必要があります。
上級:
O(1)時間の複雑さの中で両方の操作を実行できますか?
例:
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得密钥 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得密钥 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
解決策
LRUキャッシュメカニズム
Least Recently Usedの略語で、最も最近使用されていないものです。
原理
削除する最新の未使用ページを選択します。アルゴリズムは、各ページにアクセスフィールドを与えて、ページが最後にアクセスされてから経過した時間tを記録します。ページを削除する必要がある場合、最大のt値を持つ現在のページが選択されます。ページが削除されます。
メモリが3つのページサイズしか収容できないと仮定すると、ページは7 0 1 2 0 3 0 4の順序でアクセスされます。メモリがスタックに従ってアクセス時間を表していると仮定します。上部は最も最近にアクセスされたもので、下部は最も遠い時間です。LRUはこのように動作します。
アイデア
この質問は複雑なアルゴリズムを含まず、スタックを使用するだけです。O(1)時間で完了する場合はMap
、ES6のターゲットを検討してください。
JavaScriptの実装
通常版
時間の複雑さは必ずしもOではありません(1)
/**
* @param {number} capacity
*/
var LRUCache = function (capacity) {
this.capacity = capacity;
// 存放key的index
this.stack = [];
// 存放key和value
this.secretKey = {};
};
/**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function (key) {
if (key in this.secretKey) {
// 更新stack
this.stack.splice(this.stack.indexOf(key), 1);
this.stack.unshift(key);
return this.secretKey[key];
}
else return -1;
};
/**
* @param {number} key
* @param {number} value
* @return {void}
*/
LRUCache.prototype.put = function (key, value) {
// key存在,仅修改值
if (key in this.secretKey) {
this.secretKey[key] = value;
// 更新stack
this.stack.splice(this.stack.indexOf(key), 1);
this.stack.unshift(key);
}
// key不存在且栈未满
else if (this.stack.length < this.capacity) {
this.secretKey[key] = value;
this.stack.unshift(key);
}
// key不存在且栈满
else {
// 删除key
delete this.secretKey[this.stack[this.capacity - 1]]
this.secretKey[key] = value;
this.stack.pop();
this.stack.unshift(key);
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* var obj = new LRUCache(capacity)
* var param_1 = obj.get(key)
* obj.put(key,value)
*/
let cache = new LRUCache(2);
cache.put(1, 1);
cache.put(2, 2);
console.log("cache.get(1)", cache.get(1))// 返回 1
cache.put(3, 3);// 该操作会使得密钥 2 作废
console.log(cache.stack);
console.log("cache.get(2)", cache.get(2))// 返回 -1 (未找到)
cache.put(4, 4);// 该操作会使得密钥 1 作废
console.log("cache.get(1)", cache.get(1))// 返回 -1 (未找到)
console.log("cache.get(3)", cache.get(3))// 返回 3
console.log("cache.get(4)", cache.get(4))// 返回 4
Advanced Edition
ES6 Map
オブジェクトを使用する場合、時間の複雑さはOである必要があります(1)
Mapオブジェクトは、反復中にオブジェクト内の要素の挿入順序に従って実行されます
// 一个Map对象在迭代时会根据对象中元素的插入顺序来进行
// 新添加的元素会被插入到map的末尾,整个栈倒序查看
class LRUCache {
constructor(capacity) {
this.secretKey = new Map();
this.capacity = capacity;
}
get(key) {
if (this.secretKey.has(key)) {
let tempValue = this.secretKey.get(key);
this.secretKey.delete(key);
this.secretKey.set(key, tempValue);
return tempValue;
}
else return -1;
}
put(key, value) {
// key存在,仅修改值
if (this.secretKey.has(key)) {
this.secretKey.delete(key);
this.secretKey.set(key, value);
}
// key不存在,cache未满
else if(this.secretKey.size<this.capacity){
this.secretKey.set(key, value);
}
// 添加新key,删除旧key
else{
this.secretKey.set(key,value);
// 删除map的第一个元素,即为最长未使用的
this.secretKey.delete(this.secretKey.keys().next().value);
}
}
}
let cache = new LRUCache(2);
cache.put(1, 1);
cache.put(2, 2);
console.log("cache.get(1)", cache.get(1))// 返回 1
cache.put(3, 3);// 该操作会使得密钥 2 作废
console.log("cache.get(2)", cache.get(2))// 返回 -1 (未找到)
cache.put(4, 4);// 该操作会使得密钥 1 作废
console.log("cache.get(1)", cache.get(1))// 返回 -1 (未找到)
console.log("cache.get(3)", cache.get(3))// 返回 3
console.log("cache.get(4)", cache.get(4))// 返回 4
体験
私の友人はAnt Financialへのインタビュー中に同様の問題に遭遇したため、テストの確率は非常に大きくなるはずです。
get
データにアクセスすると、それは使用状況データとしてもカウントされるため、アクセス時間も更新する必要があります。