首页 > 其他分享 >LRU机制:哈希表+双向链表 [labuladong-刷题打卡 day9]

LRU机制:哈希表+双向链表 [labuladong-刷题打卡 day9]

时间:2023-08-09 20:22:58浏览次数:42  
标签:Node node day9 next 链表 tail 打卡 节点

今天的知识点LRU缓存机制的实现。学过计组都知道LRU算法(least recently used 最近最少使用算法)是资源管理中的常用算法。那么他是如何实现的呢?
LRU原理和Redis实现

图片名称

146. LRU 缓存此题算是对LRU机制的一个简化。
为了使查找删除在O(1)中实现,我们结合哈希表和双向链表各自在查找和删除插入中的优势,得到LRU的数据结构:哈希链表,其实在Linux内核中也采用了哈希链表
下面采用带你手撸 LRU 算法

哈希链表的实现

双向链表的实现

双向链表节点

//双向链表节点
class Node {
    public:
        int key, val;
        Node* next;
        Node* prev;
        Node(int k, int v){
            this->key = k;
            this->val = v;
        }
};

双向链表类

定义节点后,自然就可以构建一个双向链表,并且封装后续需要的插入删除等操作了!

class DoubleList {
private:
    // 头尾虚节点
    Node* head;
    Node* tail;
    // 链表元素数
    int size;

public:
    DoubleList() {
        // 初始化双向链表的数据
        head = new Node(0, 0);
        tail = new Node(0, 0);
        head->next = tail;
        tail->prev = head;
        size = 0;
    }

    // 在链表尾部添加节点 x,时间 O(1)
    void addLast(Node* x) {
        x->prev = tail->prev;
        x->next = tail;
        tail->prev->next = x;
        tail->prev = x;
        size++;
    }

    // 删除链表中的 x 节点(x 一定存在)
    // 由于是双链表且给的是目标 Node 节点,时间 O(1)
    void remove(Node* x) {
        x->prev->next = x->next;
        x->next->prev = x->prev;
        size--;
    }

    // 删除链表中第一个节点,并返回该节点,时间 O(1)
    Node* removeFirst() {
        if (head->next == tail)
            return nullptr;
        Node* first = head->next;
        remove(first);
        return first;
    }

    // 返回链表长度,时间 O(1)
    int getSize() { return size; }
};

哈希链表类

实现双向链表后,结合哈希表实现get,put方法
get方法主要是根据哈希表查找,如果查找到就移动节点至链表尾,并返回值
put方法也需要先查找,存在则修改值并移动至链表尾,如果不存在,但已经达到容量,则删除头节点添加新节点至链表尾
(黑体字为封装的私有函数或需要实现的功能)

class LRUCache {
private:
    /* 节点结构体 */
    struct Node {
        int key, val;
        Node* next;
        Node(int _key, int _val) : key(_key), val(_val), next(nullptr) {}
    };
    /* 哈希表存储节点指针 */
    unordered_map<int, Node*> map;
    /* 虚拟头节点 */
    Node* head;
    /* 虚拟尾节点 */
    Node* tail;
    /* 缓存容量 */
    int capacity;

public:
    LRUCache(int _capacity) : capacity(_capacity) {
        /* 初始化虚拟头尾节点 */
        head = new Node(-1, -1);
        tail = new Node(-1, -1);
        head->next = tail;
        tail->next = nullptr;
    }

    int get(int key) {
        if (!map.count(key)) {
            return -1;
        }
        /* 通过哈希表定位到节点 */
        Node* node = map[key];
        /* 将节点移动到链表尾部 */
        moveToEnd(node);
        return node->val;
    }

    void put(int key, int value) {
        if (map.count(key)) {
            /* 如果已存在,修改对应节点的值,并移动到链表尾部 */
            Node* node = map[key];
            node->val = value;
            moveToEnd(node);
            return;
        }
        if (map.size() == capacity) {
            /* 如果超出容量,删除链表头部节点 */
            deleteHead();
        }
        /* 添加新的节点到链表尾部,并在哈希表中添加映射 */
        addNode(new Node(key, value));
    }

private:
    /* 将节点移动到链表尾部 */
    void moveToEnd(Node* node) {
        removeNode(node);
        addNode(node);
    }

    /* 删除链表头部节点 */
    void deleteHead() {
        Node* node = head->next;
        removeNode(node);
        map.erase(node->key);
        delete node;
    }

    /* 添加节点到链表尾部 */
    void addNode(Node* node) {
        tail->next = node;
        node->next = nullptr;
        map[node->key] = node;
        tail = node;
    }

    /* 删除链表中的节点 */
    void removeNode(Node* node) {
        Node* prev = head;
        while (prev->next != node) {
            prev = prev->next;
        }
        prev->next = node->next;
    }
};

标签:Node,node,day9,next,链表,tail,打卡,节点
From: https://www.cnblogs.com/alanjiang/p/17617883.html

相关文章

  • 【代码设计】链表结构解决多流程校验
    目的 使用合理的代码设计,解决业务场景的中的实际问题。背景介绍 在实际的业务场景中,用户的一个操作行为,是否允许真正被执行,往往会涉及到多流程的校验,一旦有条件不满足将会被中止。以下面流程图为例:用户点击了打赏按钮,会进行是否有网络检查,没有网络,会有网络连接弹框,等待用户连接......
  • 递归反转链表局部[labuladong-刷题打卡 day8]
    写在前面前两天刷题打卡,感觉东哥的代码模板没有题解中的简洁,或者那些极限优化的代码中有很多优化技巧,但今天去感受递归的含义的时候,觉得毕竟我现在是在学习算法,理解算法含义才是学习的意义。至于优化,那是之后的事,所以刷题的时候不必过于追求简洁,就像追求简洁而降低可读性一样属......
  • 成功搞定H7-TOO的FreeRTOS Trace图形化链表方式展示任务管理
    之前推出了H7-TOOL的RTOSTrace功能,已经支持RTX5,ThreadX,uCOS-III,uCOS-II和FreeRTOS,特色是不需要目标板额外做任何代码,实时检测RTOS任务执行情况,支持在线和脱机玩法,效果是下面这样的:  这样的展示还不够直观,这几天开始研究图形化链表方式展示任务管理,从源码的角度来看,OS内核......
  • 每日一练 | 华为认证真题练习Day92
    1、TFTP基于TCP协议。A.对B.错2、Trunk类型的端口和Hybrid类型的端口在接收数据帧时的处理方式相同。A.TrueB.False3、以下哪种PPPoE的报文是非单播方式发送的?A.PADSB.PADIC.PADOD.PADR4、HDLC帧由以下哪些字段组成?(多选)A.控制字段(C)B.帧校验序列字段(FCS)C.地址字段(A)D.标......
  • 反转单向链表
    反转单向链表时间复杂度:O(N)空间复杂度:O(1)voidreverse_list(node**head_ptr){ node*prev=NULL; node*curr=*head_ptr; node*next=NULL; while(curr!=NULL){ next=curr->next; curr->next=prev; prev=curr; curr=next; } *head_......
  • 02手写链表
    一、简介手写链表实现以下功能尾插获取指定下标的元素按照指定位置插入元素打印链表内容删除指定元素释放整个链表链表反转链表中相邻元素的和最大的两个元素的第一个节点,且返回和的最大值二、源代码LinkedList.h#ifndef_LINKEDLIST_H#define_LINKEDLIST_H#inc......
  • C语言打卡练习Day4
    1.在一个有序数组中查找具体的某个数字。并将其下标打印出来intmain(){inti=0;intk=5;//要查找的数字intarr[]={1,2,3,4,5,6,7,8,9,10};intnum=sizeof(arr)/sizeof(arr[1]);for(i=0;i<num;i++){if(k==arr[i]){......
  • python对单双链表进行操作
    `classLinkNode:definit(self,val=0,next=None):#定义指针指向节点的数值self.val=val#定义指针self.next=NoneclassMyLinkedList:definit(self):self.head=LinkNode(0)self.size=0#获取链表中下标为index的值,如果下标无效,则返回-1defget(self,index:i......
  • DataWhale NLP第二期 第一次打卡
    理解赛题,跑通竞赛实践全流程跑通实践基线Baseline,获得自己的成绩提交任务一打卡,查看个人成绩排行榜赛题理解赛题链接本赛题要求构建一个文本分类器,来区分真实对话和由AI产生的对话,训练的数据包括一系列真实对话和ChatGPT生成的对话样本,参赛选手需要设计并训练一个模型,使其......
  • 8.6打卡
    L2-012关于堆的判断#include<iostream>#include<stdio.h>#include<vector>#include<cmath>#include<map>#include<algorithm>#include<queue>usingnamespacestd;intn,m,x;intheap[1001];vector<int>a;map<int,int>mp......