每日两题 / 142. 环形链表 II & 146. LRU 缓存(LeetCode热题100)

142. 环形链表 II - 力扣(LeetCode)

用哈希记录走过的节点即可

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *cur = head;
        set<ListNode*> s;
        while (cur) {
            if (s.count(cur)) {
                return cur;
            }
            s.insert(cur);
            cur = cur->next;
        }
        return nullptr;
    }
};

146. LRU 缓存 - 力扣(LeetCode)
O ( 1 ) O(1) O(1)地查找并修改kv结构,用unordered_map即可解决

问题是题目要求:哈希表容量有限,超出容量时,将删除最久未访问的kv

那么关键就在于:如何用数据结构表示访问的先后顺序?显然哈希表无法做到

越靠近链表的头指针,越经常访问该元素。为何不用数组?越靠近首元素/尾元素,越经常访问该元素?维护访问顺序时,对于数组,需要 O ( n ) O(n) O(n)地移动数组中的元素

对于链表,只需要 O ( 1 ) O(1) O(1)地修改节点,显然链表在时间上更优且满足题意

对于最近访问的元素,若不在链表中,则创建新节点并插入链表头(添加),若在链表中,则将其移动到链表头(删除+增加)。综上,我们的链表需要(增加+删除)这两个操作,显然使用双向链表更优

最后的问题是:在链表中如何 O ( 1 ) O(1) O(1)地查找某个元素?显然链表无法作用,所以使用哈希表作为辅助结构,存储key值与其在链表中的位置(节点地址)

cpp 复制代码
struct Node {
    int val, key;
    Node *prev = nullptr;
    Node *next = nullptr;
};

class LRUCache {
    
public:
    Node *head;
    Node *tail;
    unordered_map<int, Node*> mp;
    int capacity;

    void addToHead(Node *node) {
        Node *first = head->next;
        head->next = node, first->prev = node;
        node->next = first, node->prev = head;
    }

    void delNode(Node *node) {
        Node *prev = node->prev;
        Node *next = node->next;
        prev->next = next;
        next->prev = prev;
    }

    LRUCache(int capacity) {
        head = new Node;
        tail = new Node;
        head->next = tail, head->prev = tail;
        tail->next = head, tail->prev = head;
        this->capacity = capacity;
    }
    
    int get(int key) {
        if (mp.count(key)) {
            delNode(mp[key]);
            addToHead(mp[key]);
            return mp[key]->val;
        }
        return -1;
    }
    
    void put(int key, int value) {
        if (mp.count(key)) {
            mp[key]->val = value;
            delNode(mp[key]);
            addToHead(mp[key]);
        }
        else {
            mp[key] = new Node;
            mp[key]->val = value, mp[key]->key = key;
            addToHead(mp[key]);
        }
        if (mp.size() > capacity) {
            Node *Del = tail->prev;
            mp.erase(Del->key);
            delNode(Del);
            delete Del;
        }
    }
};

/**
 * 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);
 */
相关推荐
CppPlayer-程序员阿杜1 小时前
poll为什么使用poll_list链表结构而不是数组 - 深入内核源码分析
网络·c++·链表·list·poll
@hdd1 小时前
C++| 深入剖析std::list底层实现:链表结构与内存管理机制
c++·链表·list
8RTHT3 小时前
数据结构(三)---单向循环链表
数据结构·链表
小袁拒绝摆烂5 小时前
Redis-高级篇(分布式缓存/持久化)
redis·分布式·缓存
E___V___E5 小时前
黑马点评redis改 part 2
数据库·redis·缓存
Y.O.U..5 小时前
力扣HOT100——560.和为k的子数组
数据结构·c++·算法·leetcode
柃歌5 小时前
【LeetCode Solutions】LeetCode 160 ~ 165 题解
数据结构·算法·leetcode
JhonKI5 小时前
【从零实现高并发内存池】Central Cache从理解设计到全面实现
数据库·redis·缓存
珊瑚里的鱼7 小时前
【双指针】专题:LeetCode 202题解——快乐数
开发语言·c++·笔记·算法·leetcode·职场和发展
灋✘逞_兇8 小时前
快速幂+公共父节点
数据结构·c++·算法·leetcode