LRU Cache
LRU Cache
@(算法)[算法, Linked List, Zenefits, Uber, Google]
这一题怎样用一句话描述?
手动实现LRU Cache算法(Least Recent Used),思路可以参照What is the best way to implement a LRU Cache,具体LRU Cache是什么请复习计算机体系结构。
用到什么算法?什么数据结构?
Cache算法。数组,HashMap
通过这题学到了什么?
What is the best way to implement a LRU Cache中的回答是使用双向链表加上HashMap,而我用的是数组。事实上殊途同归,我们都是要将最常用的放在最前面,最不常用的放在最后面,只是实现的方法不同而已。
相较而言,我更喜欢使用数组,因为数组的swap和取值都非常方便,思路如下(Java语言描述):
新创建一个Cache,就会创建一个HashMap,其中存储了Key值,以及Key值对应的Value值在数组中的Index:
HashMap<Integer, Integer> hash = new HashMap<>();
// first Integer represents Key
// second Integer represent Index
同时,新建Cache时,还会创建一个对应其capacity大小的数组
Pair[] cache = new Pair[capacity]
你应该注意到,数组中存储的元素是一个Pair类型,因为每一个元素,我们不仅要用到value值,还需要用到key值:
private class Pair {
int key;
int val;
Pair(int key, int val) {
this.key = key;
this.val = val;
}
}
有了这两个数据结构,我们就能实现自己的Cache算法了:
- get方法: 1.1. 查询HashMap中是否包含输入的Key值,找不到返回-1;找得到则进入1.2。 1.2. 通过输入的Key值找到该元素对应在数组中的Index,将该元素移动到数组首部,其他元素依次向后挪一格,最后返回该元素值对应的Value即可。
- set方法: 2.1. 查询HashMap中是否包含输入的Key值,如果找得到,则更新其Value值,并通过输入的Key值找到该元素对应在数组中的Index,将该元素移动到数组首部,其他元素依次向后挪一格;找不到则进入2.2。 2.2. 如果当前数组中元素的个数小于capacity,则往数组中增加一个元素,在HashMap中放入Key值对应的Index值,最后将该元素移动到数组首部,其他元素依次向后挪一格;如果元素的个数已经等于capacity,则进入2.3。 2.3. 将数组最后一个位置的元素的Key值对应的HashMap中的元素删除,然后将数组最后一个位置的元素的Key值和Value值都更新,将更新后的Key值以及元素位置的Index放进HashMap,最后将该元素移动到数组首部,其他元素依次向后挪一格。
这里的将该元素移动到数组首部,其他元素依次向后挪一格实现起来也很简单,就是将相邻的数组元素swap,然后更新元素Key值对应在HashMap中的Index(一个+1,一个-1)。
总结下来就是:HashMap是用来寻址的,而数组则充当了存储的角色。当计算机需要访问某个Value时,只要传入其对应的Key;Cache算法通过Key值查询HashMap,找到元素在数组中的Index;然后返回该Index下元素的Value值即可。
参考代码:
public class Solution {
private int capacity;
private HashMap<Integer, Integer> hash;
private Pair[] cache;
private int size;
private class Pair {
int key;
int val;
Pair(int key, int val) {
this.key = key;
this.val = val;
}
}
// @param capacity, an integer
public Solution(int capacity) {
// write your code here
this.capacity = capacity;
hash = new HashMap<>();
cache = new Pair[capacity];
size = 0;
}
// @return an integer
public int get(int key) {
if (hash.containsKey(key)) {
moveToFirst(cache, hash.get(key));
return cache[hash.get(key)].val;
} else {
return -1;
}
}
// @param key, an integer
// @param value, an integer
// @return nothing
public void set(int key, int value) {
// write your code here
if (hash.containsKey(key)) {
cache[hash.get(key)].val = value;
moveToFirst(cache, hash.get(key));
return;
}
if (size < capacity) {
cache[size] = new Pair(key, value);
hash.put(key, size);
moveToFirst(cache, size);
size++;
} else {
hash.remove(cache[capacity - 1].key);
cache[capacity - 1].key = key;
cache[capacity - 1].val = value;
hash.put(key, capacity - 1);
moveToFirst(cache, capacity - 1);
}
}
private void moveToFirst(Pair[] array, int index) {
for (int i = index; i > 0 ; i--) {
swap(array, i - 1, i);
}
}
private void swap(Pair[] array, int i, int j) { // i must < j
hash.put(array[i].key, hash.get(array[i].key) + 1);
hash.put(array[j].key, hash.get(array[j].key) - 1);
Pair pair = new Pair(array[j].key, array[j].val);
array[j].key = array[i].key;
array[j].val = array[i].val;
array[i].key = pair.key;
array[i].val = pair.val;
}
}
可能(已经)遇到的BUG有?
set时,没有考虑到HashMap中已经存在Key的情况。