Last Recent Used(LRU)算法是操作系统课程接触,特点就是数据安装访问频次排列,容量到达上限时,最久未使用的节点会被替换。
难点
- 题目的难点在于
get和set都必须是的时间复杂度。 - 实现的难点在于更新双向链表,容易出错。
思路
如何按照访问顺序排布节点
双向链表:添加和移除节点时方便,以虚拟头节点为分界,链表尾部为最新数据,链表开始为最久未使用数据,能使得添加数据的过程符合题意。
如何在时间复杂度get和put节点
unordered_map:节点数量不会很多,使用hash表作为map既能不考虑key重复,又能真正在时间复杂度内找到链表进行更新。
补充
为了在移除最久未使用链表时,去unordered_map移除key,那么必须也能从链表节点获知key值。
实现
class LRUCache {
struct ValLinkNode {
int key;
int val;
ValLinkNode *next;
ValLinkNode *prev;
public:
ValLinkNode(int key, int value) :key(key), val(value), next(nullptr), prev(nullptr) {}
ValLinkNode() : key(0), val(0), next(nullptr), prev(nullptr) {};
};
public:
int m_capacity;
ValLinkNode m_head;
std::unordered_map<int, ValLinkNode*> m_valuesAddrMapping;
LRUCache(int capacity): m_capacity(capacity){
m_head.next = &m_head;
m_head.prev = &m_head;
}
void put(int key, int value) {
ValLinkNode* pNode = nullptr;
if (m_valuesAddrMapping.find(key) != m_valuesAddrMapping.end()) {
pNode = m_valuesAddrMapping[key];
pNode->prev->next = pNode->next;
pNode->next->prev = pNode->prev;
pNode->val = value;
}
else {
if (m_valuesAddrMapping.size() > (m_capacity -1)) {
pNode = m_head.next;
pNode->prev->next = pNode->next;
pNode->next->prev = pNode->prev;
m_valuesAddrMapping.erase(pNode->key);
pNode->key = key;
pNode->val = value;
}
else {
pNode = new ValLinkNode(key, value);
}
m_valuesAddrMapping[key] = pNode;
}
pNode->prev = m_head.prev;
m_head.prev->next = pNode;
m_head.prev = pNode;
pNode->next = &m_head;
}
int get(int key) {
if (m_valuesAddrMapping.find(key) == m_valuesAddrMapping.end()) { return -1; }
ValLinkNode *pNode = m_valuesAddrMapping[key];
ValLinkNode *pPrev = pNode->prev;
pPrev->next = pNode->next;
pNode->next->prev = pPrev;
pNode->prev = m_head.prev;
m_head.prev->next = pNode;
m_head.prev = pNode;
pNode->next = &m_head;
return pNode->val;
}
};
/**
* 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);
*/