diff --git a/include/s0146_lru_cache.hpp b/include/s0146_lru_cache.hpp new file mode 100644 index 0000000..7a810bd --- /dev/null +++ b/include/s0146_lru_cache.hpp @@ -0,0 +1,50 @@ +#ifndef S0146_LRU_CACHE_HPP +#define S0146_LRU_CACHE_HPP + +#include +#include + +class LRUCache { + public: + /** + * @brief Least Recently Used Cache + * + * 这是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰 + * + * @param capacity 容量,这是一个正整数 + */ + LRUCache(int capacity); + /** + * @brief 读取数据 + * + * @param key 数据对应的键值 + * @return 数据的值 + */ + int get(int key); + /** + * @brief 放入对应的数据 + * + * @param key 数据对应的键值 + * @param value 数据对应的值 + */ + void put(int key, int value); + + private: + struct CacheNode { + int key; + int value; + CacheNode *next; + CacheNode *prev; + CacheNode(int key, int value) + : key(key), value(value), next(nullptr), prev(nullptr){}; + CacheNode(int key, int value, CacheNode *next, CacheNode *prev) + : key(key), value(value), next(next), prev(prev){}; + }; + CacheNode *head; + CacheNode *tail; + int capacity; + std::unordered_map map; // 键值是 key,值是该节点的指针 + void moveToHead(CacheNode *node); // 将节点移动到头部 +}; + +#endif diff --git a/notes/src/SUMMARY.md b/notes/src/SUMMARY.md index 7450af0..8c47505 100644 --- a/notes/src/SUMMARY.md +++ b/notes/src/SUMMARY.md @@ -77,3 +77,4 @@ - [单调队列](./stack_and_queue.md) - [二叉树遍历](./btree_iter.md) - [合并两个有序链表](./merge_two_sorted_linked_lists.md) +- [LRU](./lru_cache.md) diff --git a/notes/src/lru_cache.md b/notes/src/lru_cache.md new file mode 100644 index 0000000..161440f --- /dev/null +++ b/notes/src/lru_cache.md @@ -0,0 +1,128 @@ +# LRU + +[Leetcode 146. LRU Cache](https://leetcode.cn/problems/lru-cache/description/) + +Header: + +```cpp +#ifndef S0146_LRU_CACHE_HPP +#define S0146_LRU_CACHE_HPP + +#include +#include + +class LRUCache { + public: + /** + * @brief Least Recently Used Cache + * + * 这是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰 + * + * @param capacity 容量,这是一个正整数 + */ + LRUCache(int capacity); + /** + * @brief 读取数据 + * + * @param key 数据对应的键值 + * @return 数据的值 + */ + int get(int key); + /** + * @brief 放入对应的数据 + * + * @param key 数据对应的键值 + * @param value 数据对应的值 + */ + void put(int key, int value); + + private: + struct CacheNode { + int key; + int value; + CacheNode *next; + CacheNode *prev; + CacheNode(int key, int value) + : key(key), value(value), next(nullptr), prev(nullptr){}; + CacheNode(int key, int value, CacheNode *next, CacheNode *prev) + : key(key), value(value), next(next), prev(prev){}; + }; + CacheNode *head; + CacheNode *tail; + int capacity; + std::unordered_map map; // 键值是 key,值是该节点的指针 + void moveToHead(CacheNode *node); // 将节点移动到头部 +}; + +#endif +``` + +Source: + +```cpp +#include "s0146_lru_cache.hpp" + +void LRUCache::moveToHead(CacheNode *node) { + // 如果是头部节点 + if (node == head) return; + // 如果不是头部节点,但是是尾部节点 + if (node == tail) tail = node->prev; + // 处理该节点的前后两个节点的指针 + if (node->prev) node->prev->next = node->next; + if (node->next) node->next->prev = node->prev; + // 将该节点移动到头部 + node->prev = nullptr; + node->next = head; + // 处理头部节点 + head->prev = node; + head = node; +} + +LRUCache::LRUCache(int capacity) { + this->capacity = capacity; + head = nullptr; + tail = nullptr; + map = std::unordered_map(); +} + +int LRUCache::get(int key) { + if (map.size() == 0) return -1; + if (map.count(key) == 0) return -1; + moveToHead(map[key]); + return map[key]->value; +} + +void LRUCache::put(int key, int value) { + // 如果 key 已存在,则更新 value ,并将这个节点移动到头部 + if (map.count(key) == 1) { + map[key]->value = value; + moveToHead(map[key]); + return; + } + // 否则创建该节点 + CacheNode *newNode = (CacheNode *)malloc(sizeof(CacheNode)); + newNode->value = value; + newNode->key = key; + newNode->next = head; + newNode->prev = nullptr; + // 处理头部节点 + if (head) head->prev = newNode; + head = newNode; + // 处理尾部节点 + if (map.size() == 0) tail = newNode; + // 更新哈希表 + map[key] = newNode; + // 如果容量已满 + if (map.size() > capacity) { + // 更新尾部节点 + CacheNode *node = tail; + if (tail->prev) { + tail->prev->next = nullptr; + tail = tail->prev; + } + // 移除该节点 + map.erase(node->key); + free(node); + } +} +``` diff --git a/src/s0146_lru_cache.cpp b/src/s0146_lru_cache.cpp new file mode 100644 index 0000000..db2fc2a --- /dev/null +++ b/src/s0146_lru_cache.cpp @@ -0,0 +1,65 @@ +#include "s0146_lru_cache.hpp" + +void LRUCache::moveToHead(CacheNode *node) { + // 如果是头部节点 + if (node == head) return; + // 如果不是头部节点,但是是尾部节点 + if (node == tail) tail = node->prev; + // 处理该节点的前后两个节点的指针 + if (node->prev) node->prev->next = node->next; + if (node->next) node->next->prev = node->prev; + // 将该节点移动到头部 + node->prev = nullptr; + node->next = head; + // 处理头部节点 + head->prev = node; + head = node; +} + +LRUCache::LRUCache(int capacity) { + this->capacity = capacity; + head = nullptr; + tail = nullptr; + map = std::unordered_map(); +} + +int LRUCache::get(int key) { + if (map.size() == 0) return -1; + if (map.count(key) == 0) return -1; + moveToHead(map[key]); + return map[key]->value; +} + +void LRUCache::put(int key, int value) { + // 如果 key 已存在,则更新 value ,并将这个节点移动到头部 + if (map.count(key) == 1) { + map[key]->value = value; + moveToHead(map[key]); + return; + } + // 否则创建该节点 + CacheNode *newNode = (CacheNode *)malloc(sizeof(CacheNode)); + newNode->value = value; + newNode->key = key; + newNode->next = head; + newNode->prev = nullptr; + // 处理头部节点 + if (head) head->prev = newNode; + head = newNode; + // 处理尾部节点 + if (map.size() == 0) tail = newNode; + // 更新哈希表 + map[key] = newNode; + // 如果容量已满 + if (map.size() > capacity) { + // 更新尾部节点 + CacheNode *node = tail; + if (tail->prev) { + tail->prev->next = nullptr; + tail = tail->prev; + } + // 移除该节点 + map.erase(node->key); + free(node); + } +} diff --git a/tests/s0146_lru_cache.cpp b/tests/s0146_lru_cache.cpp new file mode 100644 index 0000000..d43813d --- /dev/null +++ b/tests/s0146_lru_cache.cpp @@ -0,0 +1,26 @@ +#include "s0146_lru_cache.hpp" + +#include + +TEST(Problem146, Case1) { + LRUCache *lru = new LRUCache(2); + lru->put(1, 1); + lru->put(2, 2); + EXPECT_EQ(lru->get(1), 1); + lru->put(3, 3); + EXPECT_EQ(lru->get(2), -1); + lru->put(4, 4); + EXPECT_EQ(lru->get(1), -1); + EXPECT_EQ(lru->get(3), 3); + EXPECT_EQ(lru->get(4), 4); +} + +TEST(Problem146, Case2) { + LRUCache *lru = new LRUCache(2); + lru->put(2, 1); + lru->put(1, 1); + lru->put(2, 3); + lru->put(4, 1); + EXPECT_EQ(lru->get(1), -1); + EXPECT_EQ(lru->get(2), 3); +}