• 【算法33】LRU算法


    题目来源

    LeetCode: https://leetcode.com/problems/lru-cache/

    LRU简介

    LRU (Least Recently Used,最近最少使用)算法是操作系统中一种经典的页面置换算法,当发生缺页中断时,需要将内存的一个或几个页面换出,LRU指出应该将内存最近最少使用的那些页面进行换出,依据的是程序的局部性原理,最近经常使用的页面在不久的将来也很有可能被使用,反之最近很少使用的页面未来也不太可能再使用。

    LRU 数据结构

    LRU采用双向链表+hash表的数据结构实现,双向链表作为队列存储当前缓存节点,其中从表头到表尾的元素按照最近使用的时间进行排列,放在表头的是最近刚刚被使用过的元素,表尾的最近最少使用的元素;如果仅仅采用双向链表,那么查询某个元素需要 O(n) 的时间,为了加快双向链表中元素的查询速度,采用hash表讲key进行映射,可以在O(1)的时间内找到需要节点。

    LRU主要实现以下两个接口:

    int Get(int key);
    void Put(int key, int value);

    其中 Get 用来读取队列中的元素,同时需要将该元素移动到表头;Put 用来向队列中插入元素。

    LRU 具体实现

    从实现的角度来看,每次 Get 时, 需要判断该 key 是否在队列中,如果不在,返回-1;如果在,需要重新移动该元素到表头位置(具体实现,可以先删除,在插入到表头)。 每次 Put 时,首先需要判断key是否在队列中,如果在,那么更新其 value值,然后移动该元素到表头即可;如果不在,需要进一步判断,队列是否已满,如果已满;那么需要首先删除队尾元素,并对 size - 1, 删除哈希表中对应元素的 key;然后在插入新的元素到队头。

    具体C++代码如下:

      1 /**
      2  * LRU Cache Implementation using DoubleLinkList & hashtable
      3  * Copyright 2015 python27
      4  * 2015/06/26
      5  */
      6 #include <iostream>
      7 #include <string>
      8 #include <map>
      9 #include <list>
     10 #include <deque>
     11 #include <cassert>
     12 #include <cstdio>
     13 #include <cstdlib>
     14 using namespace std;
     15 
     16 struct CacheNode
     17 {
     18     int key;
     19     int value;
     20     CacheNode* prev;
     21     CacheNode* next;
     22     
     23     CacheNode(int k, int v) : key(k), value(v), prev(NULL), next(NULL)
     24     {}
     25     
     26     CacheNode():key(0), value(0), prev(NULL), next(NULL)
     27     {}        
     28 };
     29 
     30 class LRUCache
     31 {
     32 public:
     33     LRUCache(int capacity);
     34     
     35     int Get(int key);
     36     void Put(int key, int value);
     37     
     38 public:
     39     void PrintList() const;
     40 private:
     41     void InsertNodeFront(CacheNode* p);
     42     void DeleteNode(CacheNode* p);
     43     
     44 private:
     45     map<int, CacheNode*> m_hashtable;        // hash table
     46     CacheNode* m_head;                        // double link list head
     47     CacheNode* m_tail;                        // double link list tail
     48     int m_capacity;                            // capacity of link list
     49     int m_size;                                // current size of link list
     50 };
     51 
     52 void LRUCache::PrintList() const
     53 {
     54     CacheNode* p = m_head;
     55     for (p = m_head; p != NULL; p = p->next)
     56     {
     57         printf("(%d, %d)->", p->key, p->value);
     58     }
     59     printf("
    ");
     60     printf("size = %d
    ", m_size);
     61     printf("capacity = %d
    ", m_capacity);
     62 }
     63 
     64 LRUCache::LRUCache(int capacity)
     65 {
     66     m_capacity = capacity;
     67     m_size = 0;
     68     m_head = NULL;
     69     m_tail = NULL;
     70 }
     71 
     72 //    insert node into head pointed by p
     73 void LRUCache::InsertNodeFront(CacheNode* p)
     74 {
     75     if (p == NULL) return;
     76     
     77     if (m_head == NULL)
     78     {
     79         m_head = p;
     80         m_tail = p;
     81     }
     82     else
     83     {        
     84         p->next = m_head;
     85         m_head->prev = p;
     86         m_head = p;
     87     }
     88 }
     89 
     90 // delete node in double linklist pointed by p
     91 void LRUCache::DeleteNode(CacheNode* p)
     92 {
     93     if (p == NULL) return;
     94     
     95     assert(m_head != NULL && m_tail != NULL);
     96     
     97     if (m_size == 1)
     98     {
     99         if (p == m_head && p == m_tail)
    100         {
    101             delete p;
    102             m_head = NULL;
    103             m_tail = NULL;
    104         }
    105         else
    106         {
    107             fprintf(stderr, "Delete Wrong! No such Node");
    108             return;
    109         }
    110     }
    111     else if (p == m_head)
    112     {
    113         m_head = m_head->next;
    114         m_head->prev = NULL;
    115         delete p;
    116     }
    117     else if (p == m_tail)
    118     {
    119         m_tail = m_tail->prev;
    120         m_tail->next = NULL;
    121         delete p;
    122     }
    123     else
    124     {
    125         p->prev->next = p->next;
    126         p->next->prev = p->prev;
    127         delete p;
    128     }
    129     
    130 }
    131 
    132 int LRUCache::Get(int key)
    133 {
    134     // if key not in return -1
    135     if (m_hashtable.find(key) == m_hashtable.end())
    136     {
    137         return -1;
    138     }
    139         
    140     CacheNode* p = m_hashtable[key];
    141     int k = p->key;
    142     int v = p->value;
    143     
    144     // delete this node
    145     DeleteNode(p);
    146         
    147     // insert this node to the head
    148     p = new CacheNode(k, v);
    149     InsertNodeFront(p);
    150     // update hash table
    151     m_hashtable[k] = p;
    152     return p->value;
    153 }
    154 
    155 void LRUCache::Put(int key, int value)
    156 {
    157     // if key alread in, update
    158     if (m_hashtable.find(key) != m_hashtable.end())
    159     {
    160         CacheNode* p = m_hashtable[key];
    161         
    162         // delete node
    163         DeleteNode(p);
    164         // insert node
    165         p = new CacheNode(key, value);
    166         InsertNodeFront(p);
    167         // update hash table
    168         m_hashtable[key] = p;
    169         return;
    170     }
    171     // if list is full, delete the tail node
    172     else if (m_size >= m_capacity)
    173     {
    174         // delete the tail node
    175         CacheNode* p = m_tail;
    176         m_hashtable.erase(p->key);
    177         DeleteNode(p);
    178         m_size--;
    179     }
    180     
    181     // create node and insert into head
    182     assert(m_size < m_capacity);
    183     CacheNode* p = new CacheNode(key, value);
    184     InsertNodeFront(p);
    185     m_hashtable[key] = p;
    186     m_size++;
    187 }
    188 
    189 int main()
    190 {
    191     LRUCache lru(3);
    192     lru.Put(1, 11);
    193     lru.PrintList();
    194     lru.Put(2, 22);
    195     lru.PrintList();
    196     lru.Put(3, 33);
    197     lru.PrintList();
    198     lru.Put(4, 44);
    199     lru.PrintList();
    200     int value = lru.Get(3);
    201     printf("Get(3) = %d
    ", value);
    202     lru.PrintList();
    203     value = lru.Get(2);
    204     printf("Get(2) = %d
    ", value);
    205     lru.PrintList();
    206     value = lru.Get(4);
    207     printf("Get(4) = %d
    ", value);
    208     lru.PrintList();
    209     value = lru.Get(1);
    210     printf("Get(1) = %d
    ", value);
    211     lru.PrintList();
    212     
    213     return 0;
    214 }
  • 相关阅读:
    Metasploit的使用命令_1
    Kali Linux -系统定制
    20200522随笔
    阿里大于接口的问题
    根据一篇文章学习逻辑漏洞
    flask注册蓝图报错
    python 生成验证码
    flask-mail 机制
    对巡风vulscan的理解
    “百度杯” YeSerCMS
  • 原文地址:https://www.cnblogs.com/python27/p/LRUCache.html
Copyright © 2020-2023  润新知