每日两题 / 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);
 */
相关推荐
前端小白在前进24 分钟前
⭐力扣刷题:螺旋矩阵
算法·leetcode·矩阵
CoderYanger6 小时前
C.滑动窗口-求子数组个数-越长越合法——2799. 统计完全子数组的数目
java·c语言·开发语言·数据结构·算法·leetcode·职场和发展
圣保罗的大教堂9 小时前
leetcode 3433. 统计用户被提及情况 中等
leetcode
CoderYanger10 小时前
C.滑动窗口-求子数组个数-越长越合法——3325. 字符至少出现 K 次的子字符串 I
c语言·数据结构·算法·leetcode·职场和发展·哈希算法·散列表
sin_hielo10 小时前
leetcode 3606
数据结构·算法·leetcode
电子_咸鱼12 小时前
常见面试题——滑动窗口算法
c++·后端·python·算法·leetcode·哈希算法·推荐算法
萌>__<新12 小时前
力扣打卡每日一题————最小覆盖子串
数据结构·算法·leetcode·滑动窗口·哈希表
ada7_13 小时前
LeetCode(python)230.二叉搜索树中第k小的元素
python·算法·leetcode·链表
长安er14 小时前
LeetCode 83/237/82 链表删除问题-盒子模型
数据结构·算法·leetcode·链表·力扣
重生之后端学习14 小时前
56. 合并区间
java·数据结构·后端·算法·leetcode·职场和发展