打印

基于C++的LRUcache的循环双向链表实现

[复制链接]
775|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
韩山童|  楼主 | 2017-1-17 11:50 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
今天,偶尔有空,我们来实现LRU cache算法。LRU是啥?请问度娘去~~~
LRU实现的三个原则:
(1)越靠近链表头部的节点,表示该节点上次被访问离现在时间最短。而尾部的节点表示最近访问最少。
(2)查询或者设置节点时,若节点存在则将其插入到链表头部,若不存在则新建节点,并插入到链表头部。
(3)插入节点时,若cache的容量已经达到最大值,则将链表尾部的节点删除,新节点插入到链表头部。

OK,按照这三个节点,我们编写代码吧~~~
1.首先,定义cache中的节点:
template <typename T,typename E>
struct CacheNode  //链表中的节点
{
    T key;        //键
    E value;      //值
    CacheNode(T k,E val):key(k),value(val){};
    CacheNode<T,E>* pre;      //指向前驱节点
    CacheNode<T,E>* next;     //指向后续节点
};


2.定义LRU类,我将所有的方法都定义在类内部了,这种做**导致方法都是inline的,大家最好将比较长的方法定义在类外部。类中各个方法的实现具体不多说了,注释已经说得很清楚了,有问题回复我。OK,蟹蟹大家!!
template <typename T,typename E>
class LRUChacheList     //LRU类
{
public:
    //构造函数
    LRUChacheList(int sz,T key,E val):mSize(sz)
    {
        mHead = new  CacheNode<T,E>(key,val);
        mHead->next = nullptr;
        mHead->pre = nullptr;
        mCurrentSize = 0;
    }

    //虚析构函数
    virtual ~LRUChacheList()
    {
         if (nullptr == mHead->next) //只有头结点
         {
             delete mHead;

         }else //有多个节点
         {
            CacheNode<T,E>* p = mHead->next;
            CacheNode<T,E>* q = nullptr;
            while (p != mHead)
            {
                q = p;
                p = p->next;
                delete q;
            }
            delete mHead;
         }

    }

    //设置新值
    void setKey(T key,E value)
    {
        CacheNode<T,E>* ret=nullptr;
        if (findKey(key,ret) == false) //当前cache没有对应键的值
        {
            if (mCurrentSize == mSize)  //cache已经满 则删除链表尾部的节点
            {
                 CacheNode<T,E>* p = mHead->pre;
                 CacheNode<T,E>* q = p->pre;
                 mHead->pre = q;
                 q->next = mHead;
                 delete p;
                 --mCurrentSize;
            }

            //新建节点 并插入到链表头部
            CacheNode<T,E>* s = new  CacheNode<T,E>(key,value);
            ++mCurrentSize;
            if (mHead->next == nullptr)  //第一次插入节点时
            {
                mHead->next = s;
                s->pre = mHead;
                s->next = mHead;
                mHead->pre = s;
                return;
            }

            //第二次以后插入节点
            mHead->next->pre = s;
            s->next = mHead->next;
            mHead->next = s;
            s->pre = mHead;

        }else  //如果找到对应的键值 则更新它的值
        {
            if(nullptr != ret)
            {

                 ret->pre->next = ret->next;
                 ret->next->pre = ret->pre;

                 mHead->next->pre = ret;
                 ret->next = mHead->next;
                 mHead->next = ret;
                 ret->pre = mHead;
                 ret->value = value;
            }
        }
    }

    //获取对应键的值
    bool getValue(T& key,E& val)
    {
         CacheNode<T,E>* ret=nullptr;
         if(true == findKey(key,ret))  //找到
         {
             if(nullptr != ret)
            {
                 CacheNode<T,E>* tmp;
                 ret->pre->next = ret->next;
                 ret->next->pre = ret->pre;

                 mHead->next->pre = ret;
                 ret->next = mHead->next;
                 mHead->next = ret;
                 ret->pre = mHead;
                 val = ret->value;
            }
            return true;
         }

         //没找到
         return false;
    }

    //打印cache中的节点的值
    void print()
    {
        if (nullptr == mHead)
            return ;

        CacheNode<T,E>* p = mHead->next;
        while (p != mHead)
        {
            cout << p->value << " ";
            p = p->next;
        }
        cout << endl;
    }

    //重载<<
    ostream& operator<< (CacheNode<T,E>& s)
    {
        return cout<<s.value;
    }

protected:
    //找cache中是否存在对应的键 若存在将节点通过参数s返回 函数返回true
    bool findKey(T key,CacheNode<T,E>*& s)
    {
        if (nullptr == mHead->next)
            return false;

        CacheNode<T,E>* p = mHead->next;
        while (p != mHead)
        {
            if (key == p->key)
            {
                s = p;
                return true;
            }

            p = p->next;
        }

        return false;
    }

    //找值 并通过参数s返回改节点
    bool findValue(E val,CacheNode<T,E>*& s)
    {
        if (mHead->next == nullptr)
            return false;

        CacheNode<T,E>* p = mHead->next;
        while (p != mHead)
        {
            if (p->value == val)
            {
                s = p;
                return true;
            }
            p = p->next;
        }
        return false;
    }
private:
    int mSize;           //cache的总容量
    int mCurrentSize;     //cache的当前容量
    CacheNode<T,E>* mHead;   //链表的头结点 用于管理链表
};
3.测试:
int main()
{
    LRUChacheList<int,int> lru(5,-1,-1);
    lru.setKey(1,12);
    lru.setKey(2,13);
    lru.setKey(3,14);
    lru.setKey(4,15);
    lru.setKey(5,16);
    lru.print();
    lru.setKey(6,17);
    lru.print();
    lru.setKey(7,18);
    lru.print();

    int ret = 0;
    int key = 5;
    lru.getValue(key,ret);
    lru.print();
    lru.setKey(7,188);
    lru.print();
    return 0;
}
结果:
测试中我将cache的容量设置为5个元素。
16 15 14 13 12                     //这是新加入的5个元素,刚刚满,可以看出,最新插入的元素都在链表头部
17 16 15 14 13                     //这里表示有加入一个节点,但是cache已经满了,所以将尾部的节点(这里是12)删除,新节点插入到链表头部
18 17 16 15 14                    //同上
16 18 17 15 14                    //这里表示获取键为5(对应的值是16)的节点,然后将这个节点插入到链表头部
188 16 17 15 14                  //这里表示降键为7的节点的值修改为188,然后将该节点移动到链表头部


到此,LRU cache就实现了。程序有任何问题的请大家@我,蟹蟹!!!

相关帖子

沙发
angus118| | 2017-1-17 11:53 | 只看该作者
LRU是啥

使用特权

评论回复
板凳
韩山童|  楼主 | 2017-1-17 12:47 | 只看该作者
LRU是Least Recently Used 近期最少使用算法

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

10

主题

43

帖子

1

粉丝