03、链表:实现LRU(Least Recently Used)缓存淘汰算法

03、|链表:实现LRU(Least Recently Used)缓存淘汰算法

3.1 链表结构及其操作

与数组的对比:

内存对比

性能对比

不同的链表类型:

单链表

单链表操作

双向链表

带头链表

双向循环链表

循环链表

和单链表相比,循环链表的优点是从链尾到链头比较方便。当要处理的数据具有环型结构特点时,就特别适合采用循环链表。比如著名的约瑟夫问题——eg:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。

3.2 轻松正确的写出链表代码

一、理解指针或引用的含义

​ 含义——存储所指对象的内存地址

将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。

二、警惕指针丢失和内存泄漏

错误示范:插入节点

p -> next = x;
x -> next =p -> next;

​ 自己指向了自己,正确顺序调换一二行即可。

如果是C语言之类的,删除链表结点是,记得手动释放内存空间

三、利用哨兵简化实现难度

​ 在结点p之后插入一个新的结点:

new_node -> next =p ->next;
p -> next = new_node;

​ 但是,向一个空链表插时,就需要特数处理

if(head == null){
   
    head = new_node;
}

​ 删除结点p的后继结点

p -> next = p ->next ->next;

​ 但是,如果删除最后一个结点,上面的代码就不work了

if(head -> next == null){
   
    head == null;
}

​ 这个时候,我们引入哨兵结点,在任何时候,不管链表是不是空,head指针都会一直指向这个哨兵结点——这种链表也叫做带头链表

eg:

代码一:


// 在数组a中,查找key,返回key所在的位置
// 其中,n表示数组a的长度
int find(char* a, int n, char key) {
   
  // 边界条件处理,如果a为空,或者n<=0,说明数组中没有数据,就不用while循环比较了
  if(a == null || n <= 0) {
   
    return -1;
  }
  
  int i = 0;
  // 这里有两个比较操作:i<n和a[i]==key.
  while (i < n) {
   
    if (a[i] == key) {
   
      return i;
    }
    ++i;
  }
  
  return -1;
}                                                                         

代码二:


                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LRU (Least Recently Used) 是一种常见的页面置换算法,在操作系统被广泛使用。它的核心思想是,如果一个页面最近一段时间内没有被访问,那么它很可能在未来也不会被访问,因此可以将其替换出内存,腾出空间给其他页面使用。 下面是使用C++模拟LRU算法的示例代码: ```c++ #include <iostream> #include <list> #include <unordered_map> using namespace std; class LRUCache { public: LRUCache(int capacity) : capacity(capacity) {} int get(int key) { auto it = cache.find(key); if (it == cache.end()) { // 如果key不存在 return -1; } // 将节点移动到链表头部,并返回value cache_list.splice(cache_list.begin(), cache_list, it->second); return it->second->second; } void put(int key, int value) { auto it = cache.find(key); if (it != cache.end()) { // 如果key已经存在,更新value并将节点移动到链表头部 it->second->second = value; cache_list.splice(cache_list.begin(), cache_list, it->second); return; } if (cache.size() == capacity) { // 如果cache已满,删除链表尾部节点 auto last = cache_list.back(); cache.erase(last.first); cache_list.pop_back(); } // 在链表头部插入新节点 cache_list.emplace_front(key, value); cache[key] = cache_list.begin(); } private: int capacity; list<pair<int, int>> cache_list; // 使用双向链表保存key-value对 unordered_map<int, list<pair<int, int>>::iterator> cache; // 使用哈希表快速查找key对应的节点 }; int main() { LRUCache cache(2); cache.put(1, 1); cache.put(2, 2); cout << cache.get(1) << endl; // 返回 1 cache.put(3, 3); // 该操作会使得 key 2 作废 cout << cache.get(2) << endl; // 返回 -1 cache.put(4, 4); // 该操作会使得 key 1 作废 cout << cache.get(1) << endl; // 返回 -1 cout << cache.get(3) << endl; // 返回 3 cout << cache.get(4) << endl; // 返回 4 return 0; } ``` 在上面的代码,我们使用了双向链表和哈希表来维护LRU缓存。其,双向链表用于按照访问时间排序缓存的节点,哈希表则用于快速查找某个key对应的节点在链表的位置。当有新的key-value对要插入缓存时,如果缓存已满,则删除链表尾部的节点;如果key已经存在,则更新value并将对应的节点移动到链表头部;否则,在链表头部插入新节点。当需要从缓存获取某个key对应的value值时,我们在哈希表查找key对应的节点,将其移动到链表头部,并返回value。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值