LeetCode原题链接
明确题意
什么是LRU
缓存需要进行的操作是存和取。
不同的缓存算法主要体现在当缓存已满的时候,通过什么机制选择出需要删除的key。
LRU是Least Recently Used的缩写,即最近最少使用。
当缓存已满时删除最近最少使用的值。
解法
由于题目要求函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。 所以选择使用哈希表以及双链表的方式实现。
Coding
通过题意直接在本地通过TDD(测试驱动开发) 的方式写算法题。
首先编写测试用例如下:
package tdd.algo;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class LRUCacheTest {
@Test
void should_put_value_to_cache_and_get_success(){
LRUCache cache = new LRUCache(2);
cache.put(1, 1);
assertEquals(1, cache.get(1));
}
@Test
void should_return_default_value_if_cache_is_not_hit(){
LRUCache cache = new LRUCache(2);
assertEquals(-1, cache.get(1));
}
@Test
void should_put_value_to_cache_out_of_capacity_get_first_key_return_default_value(){
LRUCache cache = new LRUCache(2);
cache.put(1, 1);
cache.put(2, 2);
cache.put(3, 3);
assertEquals(-1, cache.get(1));
}
@Test
void should_update_cache_is_cache_already_contains_key(){
LRUCache cache = new LRUCache(2);
cache.put(1, 1);
cache.put(1, 3);
assertEquals(3, cache.get(1));
}
@Test
void should_delete_least_rare_use_key(){
LRUCache cache = new LRUCache(2);
cache.put(1, 1);
cache.put(2, 2);
assertEquals(1, cache.get(1));
cache.put(3, 3);
assertEquals(3, cache.get(3));
assertEquals(-1, cache.get(2));
assertEquals(1, cache.get(1));
}
@Test
void should_modify_value_without_make_it_wrong(){
LRUCache cache = new LRUCache(2 );
cache.put(2,2);
cache.put(1, 1);
cache.put(1, 3);
assertEquals(3, cache.get(1));
assertEquals(2, cache.get(2));
}
}
再编写代码逐个通过测试:
package tdd.algo;
import java.util.HashMap;
public class LRUCache {
HashMap<Integer, Node> map;
int capacity;
class Node{
int key;
int value;
Node left;
Node right;
Node(int key, int value){
this.key = key;
this.value = value;
}
}
Node head = new Node(-1 , -1);
Node tail = new Node(-1, -1);
LRUCache(int capacity){
this.capacity = capacity;
map = new HashMap<>(capacity);
head.right = tail;
tail.left = head;
}
public void put(int key, int value){
if(map.containsKey(key)){
Node node = map.get(key);
node.value = value;
updateToHead(node);
}else{
if(map.size() == capacity){
Node lastNode = tail.left;
deleteKey(lastNode);
map.remove(lastNode.key);
}
Node node = new Node(key, value);
map.put(key, node);
putToHead(node);
}
}
private void deleteKey(Node node) {
node.left.right = node.right;
node.right.left = node.left;
}
private void putToHead(Node node) {
node.left = head;
node.right = head.right;
node.left.right = node;
node.right.left = node;
}
private void updateToHead(Node node) {
deleteKey(node);
putToHead(node);
}
public int get(int key){
if(map.containsKey(key)){
Node node = map.get(key);
updateToHead(node);
return node.value;
}
return -1;
}
}
TestCases
要想熟悉算法题,刷一遍肯定不是够的。 当有了测试保障功能的实现时,保留测试后可以放心删除代码,重新实现以提高熟练度。
最好将刷题代码进行版本控制,如果之后想不起来可以 git reset --hard
|