算法练习- LRU实现

77 阅读1分钟

Last Recent Used(LRU)算法是操作系统课程接触,特点就是数据安装访问频次排列,容量到达上限时,最久未使用的节点会被替换。 image.png

难点

  • 题目的难点在于getset都必须是O(1)O(1)的时间复杂度。
  • 实现的难点在于更新双向链表,容易出错。

思路

如何按照访问顺序排布节点

双向链表:添加和移除节点时方便,以虚拟头节点为分界,链表尾部为最新数据,链表开始为最久未使用数据,能使得添加数据的过程符合题意。

如何在O(1)O(1)时间复杂度getput节点

unordered_map:节点数量不会很多,使用hash表作为map既能不考虑key重复,又能真正在O(1)O(1)时间复杂度内找到链表进行更新。

补充

为了在移除最久未使用链表时,去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);
 */