题目:LDR缓冲机制
答案:
参考链接:https://leetcode-cn.com/problems/lru-cache/solution/lru-ce-lue-xiang-jie-he-shi-xian-by-labuladong/
使用哈希表+双向链表
哈希表查找快,但是数据无固定顺序;链表有顺序之分,插入删除快,但是查找慢。所以结合一下,形成一种新的数据结构:哈希链表。
定义链表头表示最近操作结点,链表尾表示最久没有访问的结点。每次进行get/put操作后,都把结点插入到链表头部。
首先写伪代码
// key 映射到 Node(key, val)
HashMap<Integer, Node> map;
// Node(k1, v1) <-> Node(k2, v2)...
DoubleList cache;
int get(int key) {
if (key 不存在) {
return -1;
} else {
将数据 (key, val) 提到开头;
return val;
}
}
void put(int key, int val) {
Node x = new Node(key, val);
if (key 已存在) {
把旧的数据删除;
将新节点 x 插入到开头;
} else {
if (cache 已满) {
删除链表的最后一个数据腾位置;
删除 map 中映射到该数据的键;
}
将新节点 x 插入到开头;
map 中新建 key 对新节点 x 的映射;
}
}
import java.util.Hashtable;
class LRUCache {
class DLinkedNode { //双链表结点
int key,value;
DLinkedNode prev,next;
public DLinkedNode(int k, int v) {
this.key = k;
this.value = v;
}
}
private void addNode(DLinkedNode node) { //在链表头部添加结点
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(DLinkedNode node){ //删除某结点
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(DLinkedNode node){ //删除某结点并插入到开头
removeNode(node);
addNode(node);
}
private DLinkedNode popTail() { // 删除链表中最后一个节点,并返回该节点
DLinkedNode res = tail.prev;
removeNode(res);
return res;
}
private Hashtable<Integer, DLinkedNode> cache =
new Hashtable<Integer, DLinkedNode>();
private int size;
private int capacity;
private DLinkedNode head, tail; //头尾虚节点
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
head = new DLinkedNode(0,0);
tail = new DLinkedNode(0,0);
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
if (node == null) return -1;
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if(node == null) { //key不存在
DLinkedNode newNode = new DLinkedNode(key,value);
cache.put(key, newNode);
addNode(newNode);
++size;
if(size > capacity) {
DLinkedNode tail = popTail();
cache.remove(tail.key);
--size;
}
} else { //key存在
node.value = value;
moveToHead(node);
}
}
}
/**
* 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是HashMap的子类,LinkedHashMap 是有序的,因为 LinkedHashMap 在 HashMap 的基础上单独维护了一个具有所有数据的双向链表,该链表保证了元素迭代的顺序。
LinkedHashMap支持两种顺序插入顺序 、 访问顺序
(1)插入顺序:先添加的在前面,后添加的在后面。修改操作不影响顺序
(2)访问顺序:所谓访问指的是get/put操作,对一个键执行get/put操作后,其对应的键值对会移动到链表末尾,所以最末尾的是最近访问的,最开始的是最久没有被访问的,这就是访问顺序。
构造函数有三个参数
public LinkedHashMap(int initialCapacity, //初始容量(默认16)
float loadFactor, //加载因子(0.75)
boolean accessOrder) //指定LinkedHashMap的迭代顺序,true表示按照访问顺序,false表示插入顺序,默认false
{
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
在LinkedHashMap添加元素后,会调用removeEldestEntry防范,传递的参数是最久没有被访问的键值对,如果方法返回true,这个最久的键值对就会被删除。LinkedHashMap中的实现总返回false,该子类重写后即可实现对容量的控制