数据结构设计题

LC146.LRU缓存

手写双向链表,前后两个哨兵节点,使用哈希表快速查询

class LRUCache {
    //容量大小
    int cap = 0;
    //双向链表
    struct Node {
        int key = 0;
        int val;
        Node* prev = nullptr;
        Node* next = nullptr;
        Node(){};
        Node(int _key, int _val):key(_key), val(_val) {}
    };
    //链表的头尾作为空节点不存储值
    Node* head = nullptr, *tail = nullptr;
    //key与对应的链表值
    unordered_map<int, Node*> umap;
    //连接两个链表
    void linkTwoNode(Node* preNode, Node* laterNode) {
        preNode->next = laterNode;
        laterNode->prev = preNode;
    }
public:
    LRUCache(int capacity) {
        //初始化,创建两个新的头尾节点相连,赋值容量
        tail = new Node();
        head = new Node();
        linkTwoNode(head, tail);
        cap = capacity;
    }
    
    int get(int key) {
        if (umap.count(key)) {
            //如果存在就将新的value覆盖掉之前的
            put(key, umap[key]->val);
            return umap[key]->val;
        }
        return -1;
    }
    
    void put(int key, int value) {
        Node* newNode = nullptr;
        if (umap.count(key)) {
            //如果存在就将新的value覆盖掉之前的
            umap[key]->val = value;
            newNode = umap[key];
            //存在当前键值key所表示的数据,需要将其表示的来链表节点提前至头部节点后
            //连接当前节点的前后两个节点
            linkTwoNode(newNode->prev, newNode->next);
        } else {
            //没有找到就创建新的节点并加入哈希表
            newNode = new Node(key, value);
            umap[key] = newNode;
            //容量超出预设,清除map的记录,且需要将尾部节点的前一个节点删掉
            if (umap.size() > cap) {
                umap.erase(tail->prev->key);
                linkTwoNode(tail->prev->prev, tail);
            }
        }
        //将需要调整的节点(新节点或者是读取的旧节点)提前到头节点尾部
        linkTwoNode(newNode, head->next);
        linkTwoNode(head, newNode);
    }
};

//使用STL双向链表
class LRUCache {
    //存入list迭代器便于快速搜索
    unordered_map<int, list<pair<int, int>>::iterator> umap;
    //链表存储信息的key和value
    list<pair<int, int>> cache;
    int size;
public:
    LRUCache(int capacity) {
        size = capacity;
    }
    
    int get(int key) {
        if (umap.count(key)) {
            //如果有缓存就更新该缓存的位置
            //在cache中迭代器umap[key]的位置剪切到cache.begin()
            cache.splice(cache.begin(), cache, umap[key]);
            return umap[key]->second;
        }
        return -1;
    }
    
    void put(int key, int value) {
        if (umap.count(key)) {
            //如果有就更新该key的值以及在链表中的位置
            umap[key]->second = value;
            cache.splice(cache.begin(), cache, umap[key]);
        } else {
            //没有缓存就要在链表头加入该键值对,并让哈希表指向该节点
            cache.insert(cache.begin(), make_pair(key, value));
            umap[key] = cache.begin();
            //如果超出的容量范围就要删掉链表的尾部并去掉对应的哈希位置
            if (cache.size() > size) {
                umap.erase(cache.back().first);
                cache.pop_back();
            }
        }
    }
};

使用STL,哈希表中存储双向链表的迭代器,每次将最近使用的缓存存入到双向链表的头部

class Solution {
public:
    /**
    op
     */
    vector<int> LRU(vector<vector<int> >& op, int k) {
        list<pair<int, int>> cache;
        unordered_map<int, list<pair<int, int>>::iterator> umap;
        vector<int> res;
        for (auto &vec : op) {
            //3个数表示要更新或者查询缓存
            if (vec.size() == 3) {
                //存在这个数表明要更新这个数
                if (umap.count(vec[1])) {
                    //放到前面
                    cache.splice(cache.begin(), cache, umap[vec[1]]);
                    //重新赋值键值对
                    umap[vec[1]]->second = vec[2];
                } else {
                //不存在这个键就在链表头部插入这个键值对
                    cache.insert(cache.begin(), make_pair(vec[1], vec[2]));
                    //添加哈希映射
                    umap[vec[1]] = cache.begin();
                    //插入操作可能会超出缓存
                    if (cache.size() > k) {
                        umap.erase(cache.back().first);
                        cache.pop_back();
                    }
                }
            } else {
            //两个数表示要查询缓存,查询之后还要更新顺序
                if (umap.count(vec[1])) {
                    cache.splice(cache.begin(), cache, umap[vec[1]]);
                    res.push_back(umap[vec[1]]->second);
                }
                else res.push_back(-1);
            }
        }
         
        return res;
    }
};

LC380.O(1)时间插入删除获取随机元素

巧妙的使用一个数组及其下标进行随机数查找

class RandomizedSet {
    unordered_map<int, int> umap;
    vector<int> vec;
public:
    RandomizedSet() {

    }
    
    bool insert(int val) {
        if (umap.count(val)) return false;
        //哈希值设置成数组大小,也即该值在数组中的下标
        umap[val] = vec.size();
        vec.emplace_back(val);
        return true;
    }
    
    bool remove(int val) {
        if (!umap.count(val)) return false;
        //通过哈希表先找到这个值的下标
        int idx = umap[val];
        //这个下标改换成最后一个数,并将对应的哈希值更改
        vec[idx] = vec.back();
        umap[vec.back()] = idx;
        umap.erase(val);
        vec.pop_back();
        return true;
    }
    
    int getRandom() {
        return vec[rand() % vec.size()];
    }
};

LC716.最大栈

设计一个最大栈数据结构,既支持栈操作,又支持查找栈中最大元素。push压栈,pop返回并移除栈顶元素,top返回栈顶元素,peekMax返回栈中最大元素,popMax返回并删除栈中最大元素。

class MaxStack {
    //平衡树中的每一个节点存储一个键值对,其中“键”表示某个在栈中出现的值,“值”为一个列表
    map<int, vector<list<int>::iterator>> mp;
    list<int> stk;
public:
    MaxStack() {

    }
    
    void push(int x) {
        stk.push_front(x);
        mp[x].emplace_back(stk.begin());
    }
    
    int pop() {
        int key = stk.front();
        mp[key].pop_back();
        if (mp[key].empty()) mp.erase(key);
        stk.pop_front();
        return key;
    }
    
    int top() {
        return stk.front();
    }
    
    int peekMax() {
        return mp.rbegin()->first;
    }
    
    int popMax() {
        //取出栈顶
        int key = mp.rbegin()->first;
        //栈顶对应的数组的最后一个迭代器
        auto it = mp[key].back();
        //将该数组删除掉最后一个链表节点
        mp[key].pop_back();
        //如果已经空了就删掉
        if (mp[key].empty()) mp.erase(key);
        //删掉对应的迭代器
        stk.erase(it);
        return key;
    }
};
全部评论

相关推荐

小红书 后端选手 n*16*1.18+签字费期权
点赞 评论 收藏
分享
双非坐过牢:非佬,可以啊10.28笔试,11.06评估11.11,11.12两面,11.19oc➕offer
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务