链表:
LintCode:
public class LRUCache {
/*
* @param capacity: An integer
*/
class DlinkedNode{
int key;
int value;
DlinkedNode prev;
DlinkedNode next;
public DlinkedNode(){}
public DlinkedNode(int _key, int _value){key = _key;value=_value;}
}
Map<Integer, DlinkedNode> cache = new HashMap<>();
private int size;
private int capacity;
private DlinkedNode head, tail;
public LRUCache(int capacity) {
// do intialization if necessary
this.size = 0;
this.capacity = capacity;
head = new DlinkedNode();
tail = new DlinkedNode();
head.next = tail;
tail.prev = head;
}
/*
* @param key: An integer
* @return: An integer
*/
public int get(int key) {
// write your code here
DlinkedNode node = cache.get(key);
if(node==null){
return -1;
}
moveToHead(node);
return node.value;
}
/*
* @param key: An integer
* @param value: An integer
* @return: nothing
*/
public void set(int key, int value) {
DlinkedNode node = cache.get(key);
if(node==null){
DlinkedNode newNode = new DlinkedNode(key,value);
cache.put(key,newNode);
addToHead(newNode);
size++;
if(size>capacity){
DlinkedNode tail = removeTail();
cache.remove(tail.key);
size--;
}
}else{
node.value = value;
moveToHead(node);
}
// write your code here
}
private void addToHead(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);
addToHead(node);
}
private DlinkedNode removeTail(){
DlinkedNode res = tail.prev;
removeNode(res);
return res;
}
}
1.建立双链表-这样子可以直接删除尾部节点(最近最少使用的)和删除并移到头部节点,因为时间复杂度为1所以需要使用hashmap来直接定位;
2.双链表需要存储key和value是因为删除节点之后还需要同时删除哈希表中的key,也就是key-value也需要建立双向的联系,通过节点存储的key直接定位到哈希表的key。
3.那么构造函数应该包含一个空置 的和一个包含key value的。如果没有空置的构造函数,就无法建立新的空节点。
4.不写关键字修饰符属于默认default,也就是同一类和包都可以访问,因为随时需要访问节点的值,所以选择默认。this. 和python的self一样,指的是当前的对象。在一个方法内,如果没有出现局部变量和实例变量重名的情况下,是否使用this关键字是没有区别的。那么因此可以直接使用key(实例变量)_key(局部变量)来区分,就不需要用this。使用this就会直接访问方法外的成员变量。
class DlinkedNode{
int key;
int value;
DlinkedNode prev;
DlinkedNode next;
public DlinkedNode(){}
public DlinkedNode(int _key, int _value){key=_key;value=_value;}
}
然后是LRUCache的类的成员变量和构造函数;
1. 需要有当前的size和capacity,因为超出了就需要删除最近最少使用的也就是尾部节点。
2.有一个哈希表。(使用map接口方便后续更改具体的实现,面向对象编程)
3.头尾节点互相指向对方作为初始状态,然后进行get和set操作;
4.只需要一个构造函数,因为没有空的。
[Java] 为什么推荐使用Map map = new HashMap() 而不是 HashMap map = new HashMap() ?_善若寺的博客-CSDN博客
public class LRUCache(){
Map<Integer,DlinkedNode> cache = new HashMap<>();
private int size;
private int capacity;
private DlinkedNode head, tail;
public LRUCache(int capacity){
this.size = 0;
this.capacity = capacity;
head = new DlinkedNode();
tail = new DlinkedNode();
head.next = tail;
tail.prev = head;
}
}
然后是方法----
get-为空返回-1,否则就是返回值+(移到头部节点指向的位置,代表最近使用的-调用方法moveToHead(node))
set-如果有就是赋值新的+移到头部节点(removeNode,删除原来的节点+moveToHead)(此时此刻不会超过容量)否则就是创建新的节点+移到头部节点+判断size是否超过节点,否则就是移除尾部节点(removeTail)。
所以有更新---moveToHead(node), removeNode(node),addToHead(node),removeTail()
moveToHead包含removeNode and addToHead
然后removeTail用于返回尾部节点-用来删除哈希表的key
public void moveToHead(DlinkedNode node){
// remove and add to head
removeNode(node);
addToHead(node);
}
public void addToHead(DlinkedNode node){
// directly add to head
// double link to head.next
head.next.prev = node;
node.next = head.next;
// double link to head
head.next = node;
node.prev = head;
}
public void removeNode(DlinkedNode node){
node.next.prev = node.prev;
node.prev.next = node.next;
}
public DlinkedNode removeTail(){
DlinkedNode res = tail.prev;
removeNode(res);
return res;
}
get-为空返回-1,否则就是返回值+(移到头部节点指向的位置,代表最近使用的-调用方法moveToHead(node))
public int get(int key){
DlinkedNode node = cache.get(key);
if(node==null){
return -1;
}
// 不用写else 因为已经有return了
moveToHead(node);
return node.value;
}
public void set(int key, int value){
DlinkedNode node = cache.get(key);
if(node==null){
DlinkedNode newNode = new DlinkedNode(key,value);
addToHead(node);
cache.put(key,newNode);
size++;
if(size>capacity){
DlinkedNode end = removeTail();
cache.remove(end.key);
size--;
}
}else{
node.value = value;
moveTohead(node);
}
}
所以看着简单但是分为四部分,不熟悉太容易出错了;
再次
第一部分双链表-变量有key,value,prev and next; 两个构造函数;
第二部分LRUCache的类,hashmap cache, size和capacity,head and tail,构造函数一个,变量capacity, 然后赋值,头尾节点new一下和指向对方;
第三部分,一旦查询就是moveToHead,由removeNode和addToHead方法实现,最后删除并返回尾部节点removeTail,先removeNode再返回尾节点。
第四部分,get方法,返回key对应的节点,不为空就返回值并且moveToHead, 否则-1
set方法,返回对应节点,为空则创建新节点,addToHead,并且put key, newNode进入chache,然后size++, 然后判断容量,如果超过则removeTail,remove key from cache,然后size--, 否则就是更新value+moveToHead.