zoukankan      html  css  js  c++  java
  • 缓存算法(页面置换算法)-FIFO、LFU、LRU

    1. FIFO -- 先进先出

    如果一个数据最先进入缓存中,则应该最早淘汰掉。也就是说,当缓存满的时候,应当把最先进入缓存的数据给淘汰掉。

    实现:

    利用一个双向链表保存数据,当来了新的数据之后便添加到链表末尾,如果Cache存满数据,则把链表头部数据删除,然后把新的数据添加到链表末尾。在访问数据的时候,如果在Cache中存在该数据的话,则返回对应的value值;否则返回-1。如果想提高访问效率,可以利用hashmap来保存每个key在链表中对应的位置。

    2. LFU -- 最近最少使用

    基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路。

    LFU是基于访问次数的。

    实现:

    为了能够淘汰最少使用的数据,LFU算法最简单的一种设计思路就是利用一个数组存储数据项,用hashmap存储每个数据项在数组中对应的位置,然后为每个数据项设计一个访问频次,当数据项被命中时,访问频次自增,在淘汰的时候淘汰访问频次最少的数据。这样一来的话,在插入数据和访问数据的时候都能达到O(1)的时间复杂度,在淘汰数据的时候,通过选择算法得到应该淘汰的数据项在数组中的索引,并将该索引位置的内容替换为新来的数据内容即可,这样的话,淘汰数据的操作时间复杂度为O(n)。

      另外还有一种实现思路就是利用小顶堆+hashmap,小顶堆插入、删除操作都能达到O(logn)时间复杂度,因此效率相比第一种实现方法更加高效。

    3. LRU -- 最近最久未使用

    如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。

    实现:

    (1)用一个数组来存储数据,给每一个数据项标记一个访问时间戳,每次插入新数据项的时候,先把数组中存在的数据项的时间戳自增,并将新数据项的时间戳置为0并插入到数组中。每次访问数组中的数据项的时候,将被访问的数据项的时间戳置为0。当数组空间已满时,将时间戳最大的数据项淘汰。

    思路简单,但是需要不停地维护数据项的访问时间戳,另外,在插入数据、删除数据以及访问数据时,时间复杂度都是O(n)。

    (2)利用链表和hashmap。当需要插入新的数据项的时候,如果新数据项在链表中存在(一般称为命中),则把该节点移到链表头部;如果不存在,则新建一个节点,放到链表头部。若缓存满了,则把链表最后一个节点删除即可。在访问数据的时候,如果数据项在链表中存在,则把该节点移到链表头部,否则返回-1。这样一来在链表尾部的节点就是最近最久未访问的数据项。

    在已知要删除的节点的情况下,如何在O(1)时间复杂度内删除节点?

    假如要删除的节点是cur,通过cur可以知道cur节点的后继节点curNext,如果交换cur节点和curNext节点的数据域,然后删除curNext节点(curNext节点是很好删除地),此时便在O(1)时间复杂度内完成了cur节点的删除。

    如何使得删除末尾节点的复杂度也在O(1)?

    利用双向链表,并提供head指针和tail指针,这样一来,所有的操作都是O(1)时间复杂度。

    参考实现:

    (1)

    #include <iostream>
    #include <map>
    #include <algorithm>
    using namespace std;
     
    struct Node
    {
        int key;
        int value;
        Node *pre;
        Node *next;
    };
     
     
    class LRUCache{
    private:
        int count;
        int size ;
        map<int,Node *> mp;
        Node *cacheHead;
        Node *cacheTail;
    public:
        LRUCache(int capacity) {
          size = capacity;
          cacheHead = NULL;
          cacheTail = NULL;
          count = 0;
        }
         
        int get(int key) {
            if(cacheHead==NULL)
                return -1;
            map<int,Node *>::iterator it=mp.find(key);
            if(it==mp.end())  //如果在Cache中不存在该key, 则返回-1
            {
                return -1; 
            }
            else
            {
                Node *p = it->second;   
                pushFront(p);    //将节点p置于链表头部
            }
            return cacheHead->value;  
        }
         
        void set(int key, int value) {
            if(cacheHead==NULL)   //如果链表为空,直接放在链表头部
            {
                cacheHead = (Node *)malloc(sizeof(Node));
                cacheHead->key = key;
                cacheHead->value = value;
                cacheHead->pre = NULL;
                cacheHead->next = NULL;
                mp[key] = cacheHead;
                cacheTail = cacheHead;
                count++;
            }
            else   //否则,在map中查找
            {
                map<int,Node *>::iterator it=mp.find(key);
                if(it==mp.end())   //没有命中
                {
                    if(count == size)  //cache满了
                    {
                        if(cacheHead==cacheTail&&cacheHead!=NULL)  //只有一个节点
                        {
                            mp.erase(cacheHead->key);
                            cacheHead->key = key;
                            cacheHead->value = value;
                            mp[key] = cacheHead;
                        }
                        else
                        {
                            Node * p =cacheTail;
                            cacheTail->pre->next = cacheTail->next;  
                            cacheTail = cacheTail->pre;
     
                            mp.erase(p->key);
                         
                            p->key= key;
                            p->value = value;
                         
                            p->next = cacheHead;
                            p->pre = cacheHead->pre;
                            cacheHead->pre = p;
                            cacheHead = p;
                            mp[cacheHead->key] = cacheHead;
                        }
                    }
                    else
                    {
                        Node * p = (Node *)malloc(sizeof(Node));
                        p->key = key;
                        p->value = value;
                         
                        p->next = cacheHead;
                        p->pre = NULL;
                        cacheHead->pre = p;
                        cacheHead = p;
                        mp[cacheHead->key] = cacheHead;
                        count++;
                    }
                }
                else
                {
                    Node *p = it->second;   
                    p->value = value;
                    pushFront(p);
                }
            }
             
        }
     
         
        void pushFront(Node *cur)   //双向链表删除节点,并将节点移动链表头部,O(1)
        {
            if(count==1)
                return;
            if(cur==cacheHead)
                return;
                 
            if(cur==cacheTail)
            {
                cacheTail = cur->pre;
            }
             
            cur->pre->next = cur->next;   //删除节点
            if(cur->next!=NULL)
                cur->next->pre = cur->pre;
              
            cur->next = cacheHead;
            cur->pre = NULL;
            cacheHead->pre = cur;
            cacheHead = cur;   
        }
         
        void printCache(){
             
            Node *p = cacheHead;
            while(p!=NULL)
            {
                cout<<p->key<<" ";
                p=p->next;
            }
            cout<<endl;
        }
    };
     
     
    int main(void)
    {
        LRUCache cache(3);
        cache.set(1,1);
        //cache.printCache();
         
        cache.set(2,2);
        //cache.printCache();
         
        cache.set(3,3);
        cache.printCache();
         
        cache.set(4,4);
        cache.printCache();
         
        cout<<cache.get(4)<<endl;
        cache.printCache();
         
        cout<<cache.get(3)<<endl;
        cache.printCache();
        cout<<cache.get(2)<<endl;
        cache.printCache();
        cout<<cache.get(1)<<endl;
        cache.printCache();
         
        cache.set(5,5);
        cache.printCache();
         
        cout<<cache.get(1)<<endl;
        cout<<cache.get(2)<<endl;
        cout<<cache.get(3)<<endl;
        cout<<cache.get(4)<<endl;
        cout<<cache.get(5)<<endl;
         
        return 0;
    }
    View Code

    (2)用stl的list实现双向链表

    #include <iostream>
    #include <map>
    #include <algorithm>
    #include <list>
    using namespace std;
     
    struct Node
    {
        int key;
        int value;
    };
     
     
    class LRUCache{
    private:
        int maxSize ;
        list<Node> cacheList;
        map<int, list<Node>::iterator > mp;
    public:
        LRUCache(int capacity) {
          maxSize = capacity;
        }
         
        int get(int key) {
            map<int, list<Node>::iterator >::iterator it = mp.find(key);
            if(it==mp.end())      //没有命中
            {
                return -1;
            }
            else  //在cache中命中了
            {
                list<Node>::iterator listIt = mp[key];
                Node newNode;
                newNode.key = key;
                newNode.value = listIt->value;
                cacheList.erase(listIt);               //先删除命中的节点      
                cacheList.push_front(newNode);   //将命中的节点放到链表头部
                mp[key] = cacheList.begin();
            }
            return cacheList.begin()->value;
        }
         
        void set(int key, int value) {
            map<int, list<Node>::iterator >::iterator it = mp.find(key);
            if(it==mp.end())   //没有命中
            {
                if(cacheList.size()==maxSize)  //cache满了
                {
                    mp.erase(cacheList.back().key);    
                    cacheList.pop_back();
                }
                Node newNode;
                newNode.key = key;
                newNode.value = value;
                cacheList.push_front(newNode);
                mp[key] = cacheList.begin();
            }
            else  //命中
            {
                list<Node>::iterator listIt = mp[key];
                cacheList.erase(listIt);               //先删除命中的节点          
                Node newNode;
                newNode.key = key;
                newNode.value = value;
                cacheList.push_front(newNode);   //将命中的节点放到链表头部
                mp[key] = cacheList.begin();
            }
        }
    };
     
     
    int main(void)
    {
        LRUCache cache(3);
        cache.set(1,1);
         
        cache.set(2,2);
         
        cache.set(3,3);
         
        cache.set(4,4);
         
        cout<<cache.get(4)<<endl;
         
        cout<<cache.get(3)<<endl;
        cout<<cache.get(2)<<endl;
        cout<<cache.get(1)<<endl;
         
        cache.set(5,5);
         
        cout<<cache.get(1)<<endl;
        cout<<cache.get(2)<<endl;
        cout<<cache.get(3)<<endl;
        cout<<cache.get(4)<<endl;
        cout<<cache.get(5)<<endl;
         
        return 0;
    }
    View Code
  • 相关阅读:
    爱情三十七课,恩情仪式
    爱情三十二课,幽默的用法
    爱情四十二课,距离就是问题
    爱情二十八课,你为什么爱
    爱情三十四课,放手的时机
    爱情三十九课,爱的礼物
    爱情三十三课,读懂愤怒
    爱情三十一课,先信自己
    爱情三十课,爱情整理术
    爱情二十四课,妥协50分
  • 原文地址:https://www.cnblogs.com/argenbarbie/p/5401133.html
Copyright © 2011-2022 走看看