leetcode146.LRU缓存机制(中等)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思路一:hash+set 复杂度O(logn) 复杂度主要来源于红黑树的插入和删除
键->值:hash
删除最久未使用:set<pair<int, int>> st; 需要删除最久未使用的键值对,所以first存键,second存出现次数。对st排序时,pair按照second递增。

class LRUCache {
public:
    struct cmp{
        bool operator()(pair<int, int> &p1, pair<int, int> &p2) const{
            return p1.second < p2.second;
        }
    };
    unordered_map<int, int> ump; //键值
    set<pair<int, int>, cmp> st; //键;时间,按照时间升序
    unordered_map<int, int> st2; //为st的拷贝,方便st删除元素
    int cnt = 0;
    int len;
    LRUCache(int capacity) {
        len = capacity;
    }

    int get(int key) { //随机访问键
        if (ump.count(key)) {
            //删除st 加入st
            st.erase(make_pair(key, st2[key]));
            st.insert(make_pair(key, cnt));
            //修改st2
            st2[key] = cnt++;
        }
        return ump.count(key) ? ump[key] : -1;
    }

    void put(int key, int value) {
        if (ump.count(key)) {//存在
            //删除st 加入st
            st.erase(make_pair(key, st2[key]));
            st.insert(make_pair(key, cnt));
            //修改st2
            st2[key] = cnt++;
            ump[key] = value;
        }else {
            if (ump.size() >= len) {
                // ump删除 st2删除 st删除
                ump.erase(st.begin()->first);
                st2.erase(st.begin()->first);
                st.erase(st.begin());

            }
            st.insert(make_pair(key, cnt));
            st2[key] = cnt++;
            ump[key] = value;
        }
    }
};

思路上的错误:
1.get函数对一个键获得后,它应该更改为最近访问的!
1.当put存在的键时,不应该先判断ump是否超过len然后删掉最近未使用的,应该只有当put不存在的键是才判断!

思路二:hash+list O(1)
1.键->值:hash
删除最近未使用,存在先后顺序:栈,队列,链表。然而当对一个键进行get后,要将该值的放到最近,用栈和队列的话,必须把它左边的都出去,所以栈和队列不适用。当对一个键进行get后,需要将一个结点插入到某一个节点后面,所以用双向链表,链头是最久未使用的,链尾是最近刚使用的。
2. 由于在链表中删除指定节点,复杂度达不到O(1),所以在hash中存储双向链表中结点的地址

class LRUCache {
public:

    unordered_map<int, list<pair<int, int>> :: iterator> ump; //值->链表结点地址
    list<pair<int, int>> lst; 
    //存键->值
    int len;
    LRUCache(int capacity) {

        len = capacity;
    }
    
    int get(int key) {
        
        if (ump.count(key)) {
            lst.push_back(make_pair(key, ump[key]->second));
            lst.erase(ump[key]);
            ump[key] = prev(lst.end());
        }
        return ump.count(key) ? ump[key]->second : -1; 
    }
    
    void put(int key, int value) {
        
        if (ump.count(key)) {
            lst.push_back(make_pair(key, value));
            lst.erase(ump[key]);
            ump[key] = prev(lst.end());
        }else {
            if (ump.size() >= len) {
                ump.erase(lst.begin()->first);
                lst.erase(lst.begin());
            }
            lst.push_back(make_pair(key, value));
             ump[key] = prev(lst.end());
        }
    }
};

代码上的难点:
1.链表节点存的值:由于删除最久未访问节点时,也需要从hash中删除,所以链表中要存键,get需要获取键对应的值,所以也需要存值,也就是pair<int, int>;
2.链表结点的类型:list<pair<int, int>> :: iterator
3.erase只能删除前向迭代器,想删除最后一个元素
在双向链表中节点地址不连续,所以不能用lst.end() - 1,只能用prev(lst.end())

思路三:手写双向链表 面试会考
思路:由于需要在尾插节点,所以需要维护一个尾巴,需要删掉最久未使用,所以也需要维护一个头,搞一个有头有尾的双链表

class LRUCache {
public:
    struct node {
        int k;
        int v;
        node *before;
        node *after; 
        node(int _k, int _v) : k(_k), v(_v) {}
        node(int _k, int _v, node *_before, node *_after) : 
        k(_k), v(_v), before(_before), after(_after){}
    };
    unordered_map<int, node*> ump;
    int len;
    node *head, *tail;
    LRUCache(int capacity) {
        
        len = capacity;
        head = new node(-1, -1);
        tail = new node(-2, -2);
        head->after = tail;
        tail->before = head;
    }
    
    int get(int key) {
        if (ump.count(key)) {
            move(key);
        }
        return ump.count(key) ? ump[key]->v : -1;
    }
    
    void put(int key, int value) {
        if (ump.count(key)) {
            move(key);
            ump[key]->v = value;
        }else {
            node *tmp;
            if (ump.size() >= len) {
                tmp = head->after;
                head->after = tmp->after;
                tmp->after->before = head;
                ump.erase(tmp->k);
                delete tmp;
            }
            ump[key] = new node(key, value, tail->before, tail);
            ump[key]->before->after = ump[key];
            tail->before = ump[key];

        }
    }
    void move(int key) { //将key对应的节点移到最近访问的位置(tail前面)

        ump[key]->after->before = ump[key]->before;
        ump[key]->before->after = ump[key]->after;
        node* tmp = tail->before;
        ump[key]->after = tail;
        ump[key]->before = tmp;
        tail->before = ump[key];
        tmp->after = ump[key];

    }
};

简化代码:双链表可以写这样一个构造函数:
node(int _k, int _v, node *_before, node *_after)
同时指向前后节点

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值