题目描述
// 146. LRU 缓存机制
// 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
// 实现 LRUCache 类:
// LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
// int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
// void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不
// 存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删
// 除最久未使用的数据值,从而为新的数据值留出空间。
//
// 进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?
题解
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
// 数组维护
// 这道题难点在于,如何记录数据“被使用”,衡量数据“多久被使用”,并根据
// 记录的数据被使用频次,在put进新数据超过容量时将使用频次最少的数据去除。
//
// 我这里用了一个数组来维护数据的使用频次,最左边的数据使用频次最旧,
// 最右边的数据使用频次最新。同时数组可以依靠元素的序号,对相同key的
// 数据覆盖造成的使用频次刷新进行一个更新。
//
// 我用hashmap map提供基本的put和get功能,用ArrayList usedLog来记录数据
// 的使用频次。size来记录当前map的数据存放数量。
///对于put数据来说,
// 如果map中本来就有key-value这个键值对,那在map上对key对应的value进行一个
// 覆盖。由于key对应的值被覆盖了,使用频次更新到最新,所以删除原来usedLog
// 中的key,在将key添加到usedLog末尾。size不变
// 如果map中不存在key-value这个键值对,先进行容量判断,如果容量没有
// 超过capacity,map加入键值对,usedLog将key add进去。size累加一次。
// 如果容量超过了capacity,将usedLog排在最左边的最不经常使用的元素从
// map中去除,从usedLog中去除。
// 对于get数据来说,如果map不存在这个key,直接返回-1;
// 如果存在这个key,在usedLog中更新这个数据,将key删掉之后再add放入usedLog
// 的最右边。然后返回map中这个key的value。
//
// 执行用时:171 ms, 在所有 Java 提交中击败了5.04%的用户
// 内存消耗:46.4 MB, 在所有 Java 提交中击败了79.96%的用户
import java.util.HashMap;
import java.util.ArrayList;
class LRUCache {
public int capacity = 0;
private int size = 0;
Map<Integer, Integer> map;
ArrayList<Integer> usedLog;
public LRUCache(int capacity) {
this.capacity = capacity;
this.map = new HashMap<>();
this.usedLog = new ArrayList<>();
}
public int get(int key) {
if (!map.containsKey(key))
return -1;
else {
usedLog.remove(usedLog.indexOf(key));
usedLog.add(key);
return map.get(key);
}
}
public void put(int key, int value) {
if (map.containsKey(key)) {
map.put(key, value);
usedLog.remove(usedLog.indexOf(key));
usedLog.add(key);
}
else {
if (size + 1 > capacity) {
map.remove(usedLog.get(0));
usedLog.remove(0);
}
map.put(key, value);
usedLog.add(key);
this.size++;
}
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
// LinkedHashMap维护
// 最好的办法是用LinkedHashMap维护,LinkedHashMap本身能够记录插入元素的
// 顺序,那么我们就可以用在数组维护中一样的办法。如果是靠最左边的数据,
// 是最不常使用的数据,靠最右边的是最常使用的数据。思路都是一样的。
// 定义一个makeRecently函数,用于更新key值的使用频次到最新使用过。
// 其实就是将map中的key-value键值对删掉,再put进去就行了。此时key-value
// 就放置于map的最右端了。
// 这样的话就简单了,get函数如果不存在key,返回-1,存在key,则更新一下
// key的使用频次到最新,然后返回map.get(key)。
// put函数,如果key已存在,做一个key-value的覆盖,然后更新key使用频次到
// 最新。如果key-value不存在,判断容量,如果容量超了,迭代器取左数第一个
// 键值对删除掉,也就是删掉使用频次最旧的键值对。然后put入最新的键值对。
// 容量没超就直接put入键值对即可。
//
// 执行用时:21 ms, 在所有 Java 提交中击败了45.30%的用户
// 内存消耗:46.3 MB, 在所有 Java 提交中击败了85.38%的用户
class LRUCache {
int capacity;
LinkedHashMap<Integer, Integer> map;
public LRUCache(int capacity) {
this.capacity = capacity;
this.map = new LinkedHashMap<>();
}
public int get(int key) {
if (!map.containsKey(key)) {
return -1;
}
makeRecently(key);
return map.get(key);
}
public void put(int key, int value) {
if (map.containsKey(key)) {
map.put(key, value);
makeRecently(key);
}
else {
if (map.size() >= this.capacity) {
int oldestKey = map.keySet().iterator().next();
map.remove(oldestKey);
}
map.put(key, value);
}
}
private void makeRecently(int key) {
int val = map.get(key);
map.remove(key);
map.put(key, val);
}
}