缓存算法(页面置换算法)-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 
#include 
#include 
using namespace std;
 
struct Node
{
    int key;
    int value;
    Node *pre;
    Node *next;
};
 
 
class LRUCache{
private:
    int count;
    int size ;
    map 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::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::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<key<<" ";
            p=p->next;
        }
        cout<


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

#include 
#include 
#include 
#include 
using namespace std;
 
struct Node
{
    int key;
    int value;
};
 
 
class LRUCache{
private:
    int maxSize ;
    list cacheList;
    map::iterator > mp;
public:
    LRUCache(int capacity) {
      maxSize = capacity;
    }
     
    int get(int key) {
        map::iterator >::iterator it = mp.find(key);
        if(it==mp.end())      //没有命中
        {
            return -1;
        }
        else  //在cache中命中了
        {
            list::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::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::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)