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

[复制链接]
1116|2
 楼主| 韩山童 发表于 2017-1-17 11:50 | 显示全部楼层 |阅读模式
今天,偶尔有空,我们来实现LRU cache算法。LRU是啥?请问度娘去~~~
LRU实现的三个原则:
(1)越靠近链表头部的节点,表示该节点上次被访问离现在时间最短。而尾部的节点表示最近访问最少。
(2)查询或者设置节点时,若节点存在则将其插入到链表头部,若不存在则新建节点,并插入到链表头部。
(3)插入节点时,若cache的容量已经达到最大值,则将链表尾部的节点删除,新节点插入到链表头部。

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


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

  13.     //虚析构函数
  14.     virtual ~LRUChacheList()
  15.     {
  16.          if (nullptr == mHead->next) //只有头结点
  17.          {
  18.              delete mHead;

  19.          }else //有多个节点
  20.          {
  21.             CacheNode<T,E>* p = mHead->next;
  22.             CacheNode<T,E>* q = nullptr;
  23.             while (p != mHead)
  24.             {
  25.                 q = p;
  26.                 p = p->next;
  27.                 delete q;
  28.             }
  29.             delete mHead;
  30.          }

  31.     }

  32.     //设置新值
  33.     void setKey(T key,E value)
  34.     {
  35.         CacheNode<T,E>* ret=nullptr;
  36.         if (findKey(key,ret) == false) //当前cache没有对应键的值
  37.         {
  38.             if (mCurrentSize == mSize)  //cache已经满 则删除链表尾部的节点
  39.             {
  40.                  CacheNode<T,E>* p = mHead->pre;
  41.                  CacheNode<T,E>* q = p->pre;
  42.                  mHead->pre = q;
  43.                  q->next = mHead;
  44.                  delete p;
  45.                  --mCurrentSize;
  46.             }

  47.             //新建节点 并插入到链表头部
  48.             CacheNode<T,E>* s = new  CacheNode<T,E>(key,value);
  49.             ++mCurrentSize;
  50.             if (mHead->next == nullptr)  //第一次插入节点时
  51.             {
  52.                 mHead->next = s;
  53.                 s->pre = mHead;
  54.                 s->next = mHead;
  55.                 mHead->pre = s;
  56.                 return;
  57.             }

  58.             //第二次以后插入节点
  59.             mHead->next->pre = s;
  60.             s->next = mHead->next;
  61.             mHead->next = s;
  62.             s->pre = mHead;

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

  67.                  ret->pre->next = ret->next;
  68.                  ret->next->pre = ret->pre;

  69.                  mHead->next->pre = ret;
  70.                  ret->next = mHead->next;
  71.                  mHead->next = ret;
  72.                  ret->pre = mHead;
  73.                  ret->value = value;
  74.             }
  75.         }
  76.     }

  77.     //获取对应键的值
  78.     bool getValue(T& key,E& val)
  79.     {
  80.          CacheNode<T,E>* ret=nullptr;
  81.          if(true == findKey(key,ret))  //找到
  82.          {
  83.              if(nullptr != ret)
  84.             {
  85.                  CacheNode<T,E>* tmp;
  86.                  ret->pre->next = ret->next;
  87.                  ret->next->pre = ret->pre;

  88.                  mHead->next->pre = ret;
  89.                  ret->next = mHead->next;
  90.                  mHead->next = ret;
  91.                  ret->pre = mHead;
  92.                  val = ret->value;
  93.             }
  94.             return true;
  95.          }

  96.          //没找到
  97.          return false;
  98.     }

  99.     //打印cache中的节点的值
  100.     void print()
  101.     {
  102.         if (nullptr == mHead)
  103.             return ;

  104.         CacheNode<T,E>* p = mHead->next;
  105.         while (p != mHead)
  106.         {
  107.             cout << p->value << " ";
  108.             p = p->next;
  109.         }
  110.         cout << endl;
  111.     }

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

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

  123.         CacheNode<T,E>* p = mHead->next;
  124.         while (p != mHead)
  125.         {
  126.             if (key == p->key)
  127.             {
  128.                 s = p;
  129.                 return true;
  130.             }

  131.             p = p->next;
  132.         }

  133.         return false;
  134.     }

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

  140.         CacheNode<T,E>* p = mHead->next;
  141.         while (p != mHead)
  142.         {
  143.             if (p->value == val)
  144.             {
  145.                 s = p;
  146.                 return true;
  147.             }
  148.             p = p->next;
  149.         }
  150.         return false;
  151.     }
  152. private:
  153.     int mSize;           //cache的总容量
  154.     int mCurrentSize;     //cache的当前容量
  155.     CacheNode<T,E>* mHead;   //链表的头结点 用于管理链表
  156. };
3.测试:
  1. int main()
  2. {
  3.     LRUChacheList<int,int> lru(5,-1,-1);
  4.     lru.setKey(1,12);
  5.     lru.setKey(2,13);
  6.     lru.setKey(3,14);
  7.     lru.setKey(4,15);
  8.     lru.setKey(5,16);
  9.     lru.print();
  10.     lru.setKey(6,17);
  11.     lru.print();
  12.     lru.setKey(7,18);
  13.     lru.print();

  14.     int ret = 0;
  15.     int key = 5;
  16.     lru.getValue(key,ret);
  17.     lru.print();
  18.     lru.setKey(7,188);
  19.     lru.print();
  20.     return 0;
  21. }
结果:
测试中我将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

粉丝
快速回复 在线客服 返回列表 返回顶部