一、缓存是什么算法
二、什么是LRU缓存编程
三、LRU缓存设计思路缓存
四、LRU缓存接口服务器
其实缓存的思想在生活中到处可见,好比说咱们的书架上摆满了各类书籍,可是我最近想集中精力攻克数据结构这门课,因次为了方便,我将与数据结构有关的书先从书架取下来,放在咱们的书桌上,这样咱们取书的时候就快不少了,伸手可得,而不用跑到书架旁取书了,在这个例子里,书桌所扮演的就是缓存的角色。网络
在计算机中,咱们知道,访问磁盘的效率比访问内存的效率低得多,低多少了,大概是10W级别吧,以下图所示。
咱们知道有个很著名的二八定律。其实计算机世界中也有不少状况遵照这个定律,好比说网站访问的特色:80% 的业务访问集中在20% 的数据上。那么很天然就想到:既然大部分业务访问集中在一小部分数据上,那么若是经过设计合理的数据结构把这一小部分数据存放在内存中,是否是极大的提升了系统的性能呢,这就是缓存。数据结构
从上面能够看出,不管是生活中仍是计算机世界中的缓存,主要有如下两个特色:并发
提升系统效率,ide
因为内存是有限的,所以缓存总有满的时候,那么当缓存满了的时候,这时候又有新的数据须要加入到缓存中时,咱们该怎么办了?没什么别的办法,只有从缓存中删除旧数据为新数据腾出空间,那么究竟删除原来缓存的哪些数据了?这就涉及到缓存的替换策略,LRU就是一种缓存策略。函数
LRU,Least Recently Used的简写,翻译过来就是“最近最少使用”。该算法依据于程序的局部性原理, 其淘汰旧数据的策略是,距离当前最久没有被访问过的数据应该被淘汰。性能
举个例子:如今有一个能存放5个数据的缓存,刚开始中缓存中存放了3个数据A->B->C,以下所示:
接着又在缓存中插入2个数据D->E,此时缓存已经满了。以下:
好了,如今咱们来访问数据A,如今A成为了最近访问的数据,B成了最久没有访问过的数据:
如今咱们又有一个数据F想要插入到缓存中,因为缓存已经满了,须要从缓存中淘汰旧数据,此时缓存中的数据B是距离当前时间最久没有被访问过的数据,根据LRU算法,咱们会淘汰掉数据B:将F插入到当前B的位置。
此时数据C成为了距离当前时间最久没有被访问过的数据,所以若是再来一个数据的话,就应该插入到C当前的位置,依次类推。
理解LRU缓存的思想其实挺简单,可是要本身动手设计一个LRU缓存就并不那么简单了,这节主要讲讲LRU缓存的设计思路。
首先从前面的分析能够看出,LRU缓存主要的操做是插入删除以及查找,所以咱们能够考虑用双链表来维护缓存,该链表将缓存数据按访问时间重新到旧排列起来。
若是咱们只是单存将缓存中的数据维护在一个双向链表中, 那么当咱们须要从缓存中查找数据时,须要遍历链表,其时间复杂度为O(n)。 这样的设计, 是一种比较低效率的作法。 所以, 咱们除了将数据维护在双向链表中, 咱们同时还将数据维护在一个哈希表中。 哈希表访问数据的时间复杂度为O(1)。
依据上述设计, 一个LRU缓存包含了一个双向链表和一个哈希表, 双向链表以及哈希表的一个节点表明缓存中的一个缓存单元, 所以咱们能够这样定义咱们缓存单元的数据结构:
//LRU缓存的缓存单元 typedef struct cacheEntryS { char key; //数据的key char data; // 数据的data struct cacheEntryS *hashListPrev; //指向哈希链表的前一个元素 struct cacheEntryS *hashListNext; //指向哈希链表的后一个元素 struct cacheEntryS *lruListPrev; //指向链表的前一个元素 struct cacheEntryS *lruListNext; //指向链表后一个元素 }cacheEntryS;
LRU缓存的数据结构以下:
//定义LRU缓存 typedef struct LRUCacheS { int cacheCapacity; //缓存的容量 cacheEntryS **hashMap; //缓存的哈希表 cacheEntryS *lruListHead;//缓存的双向链表表头 cacheEntryS *lruListTail;//缓存的双向链表表尾 int lruListSize; //缓存的双向链表节点个数 }LRUCacheS;
若是一大坨代码看的比较晕,那么对着下面的结构关系图应该能理解的比较透彻,注意下图中没有指向的指针都是NULL:
经过前面的分析不难看出,对一个LRU缓存的最重要的操做无外乎是建立、销毁、插入数据、查找数据。下面给出这几个接口,至于具体实现,这里先不分析。后面有机会再一一分析。
/********************************************* 函数名:LRUCacheCreate 功能:建立LRU缓存 输入参数:capacity,缓存的数据容量 输出参数:lruCache,指向新建缓存的指针 针返回值:0---成功 -1---失败 *********************************************/ int LRUCacheCreate(int capacity, void **lruCache);
/********************************************* 函数名:LRUCacheDestory 功能:销毁LRU缓存 输入参数:lruCache指向新建缓存的指针 输出参数:无 针返回值:0---成功 -1---失败 *********************************************/ int LRUCacheDestory(void *lruCache);
/********************************************* 函数名:LRUCacheSet 功能:将数据插入到LRU缓存中 输入参数:key:数据索引 data:数据内容 输出参数:无 针返回值:0---成功 -1---失败 *********************************************/ int LRUCacheSet(void *lruCache, char key, char data);
/********************************************* 函数名:LRUCacheGet 功能:将数据插入到LRU缓存中 输入参数:key:数据索引 输出参数:无 针返回值:缓存中存在key对应的data,返回 缓存中不存在key对应的data,返回'\0' *********************************************/ int LRUCacheGet(void *lruCache, char key);
【福利】本身搜集的网上精品课程视频分享(上)
【协议森林】邮差与邮局 (网络协议概观)
【数据结构与算法】 通俗易懂讲解 位排序
【C++札记】C++11并发编程(一)开启线程之旅
【C++札记】C/C++指针使用常见的坑
【C++札记】静态库和动态库详解(上)
码农有道,为您提供通俗易懂的技术文章,让技术变的更简单!