zoukankan      html  css  js  c++  java
  • LRU Cache的简单c++实现

    什么是 LRU


    LRU Cache是一个Cache的置换算法,含义是“最近最少使用”,把满足“最近最少使用”的数据从Cache中剔除出去,并且保证Cache中第一个数据是最近刚刚访问的,因为这样的数据更有可能被接下来的程序所访问。

    LRU的应用比较广泛,最基础的内存页置换中就用了,对了,这里有个概念要清楚一下,Cache不见得是CPU的高速缓存的那个Cache,这里的Cache直接翻译为缓存,就是两种存储方式的速度有比较大的差别,都可以用Cache缓存数据,比如硬盘明显比内存慢,所以常用的数据我们可以Cache在内存中。

    LRU基本算法描述


    前提:

    • 由于我只是简单实现一下这个算法,所以数据都用int代替,下一个版本会改成模板形式的,更加通用。

    要求:

    • 只提供两个接口,一个获取数据getValue(key),一个写入数据putValue(key,value)
    • 无论是获取还是写入数据,当前这个数据要保持在最容易访问的位置
    • 缓存数量有限,最长时间没被访问的数据应该置换出缓存

    算法:

    为了满足上面几个条件,实际上可以用一个双向链表来实现,每次访问完数据(不管是获取还是写入),调整双向链表的顺序,把刚刚访问的数据调整到链表的最前方,以后再访问的时候速度将最快。

    为了方便,提供一个头和一个尾节点,不存具体的数,链表的基本形式如下面的这个简单表述

    Head <===> Node1 <===> Node2 <===> Node3 <===> Near


    OK,就这么些,比较简单,实现起来也不难,用c++封装一个LRUCache类,类提供两个方法,分别是获取和更新,初始化类的时候传入Cache的节点数。


    先定义一个存数据的节点数据结构


    typedef struct _Node_{
    
    	int key;    //键
    	int value;  //数据
    	
    	struct _Node_ *next;  //下一个节点
    	struct _Node_ *pre;   //上一个节点
    
    }CacheNode;


    类定义:


    class LRUCache{
    	
    public:
    	
    	LRUCache(int cache_size=10);  //构造函数,默认cache大小为10
    	~LRUCache();      //析构函数
    
    
    	int getValue(int key);	           //获取值
    	bool putValue(int key,int value);  //写入或更新值	
    	void displayNodes();               //辅助函数,显示所有节点
    	
    	
    private:
    	
    	int cache_size_;                   //cache长度
    	int cache_real_size_;              //目前使用的长度
    	CacheNode *p_cache_list_head;      //头节点指针
    	CacheNode *p_cache_list_near;      //尾节点指针
    		
    	void detachNode(CacheNode *node);  //分离节点
    	void addToFront(CacheNode *node);  //将节点插入到第一个
    
    };


    类实现:

    LRUCache::LRUCache(int cache_size)
    {
    	cache_size_=cache_size;
    	cache_real_size_=0;
    	p_cache_list_head=new CacheNode();
    	p_cache_list_near=new CacheNode();
    	p_cache_list_head->next=p_cache_list_near;
    	p_cache_list_head->pre=NULL;
    	p_cache_list_near->pre=p_cache_list_head;
    	p_cache_list_near->next=NULL;
    	
    }
    
    LRUCache::~LRUCache()
    {
    	CacheNode *p;
    	p=p_cache_list_head->next;
    	while(p!=NULL)
    	{	
    		delete p->pre;
    		p=p->next;
    	}
    
    	delete p_cache_list_near;
    	
    }
    
    
    void LRUCache::detachNode(CacheNode *node)
    {
    	node->pre->next=node->next;
    	node->next->pre=node->pre;
    }
    
    void LRUCache::addToFront(CacheNode *node)
    {
    	node->next=p_cache_list_head->next;
    	p_cache_list_head->next->pre=node;
    	p_cache_list_head->next=node;
    	node->pre=p_cache_list_head;
    }
    
    
    int LRUCache::getValue(int key)
    {
    	CacheNode *p=p_cache_list_head->next;	
    	while(p->next!=NULL)
    	{
    		
    		if(p->key == key) //catch node
    		{
    			
    			detachNode(p);
    			addToFront(p);
    			return p->value;
    		}	
    		p=p->next;	
    	}
    	return -1;
    }
    
    
    
    bool LRUCache::putValue(int key,int value)
    {
    	CacheNode *p=p_cache_list_head->next;
    	while(p->next!=NULL)
    	{
    		
    		
    		if(p->key == key) //catch node
    		{
    			p->value=value;
    			getValue(key);
    			return true;
    		}	
    		p=p->next;	
    	}
    	
    	
    	if(cache_real_size_ >= cache_size_)
    	{
    		cout << "free" <<endl;
    		p=p_cache_list_near->pre->pre;
    		delete p->next;
    		p->next=p_cache_list_near;
    		p_cache_list_near->pre=p;
    	}
    	
    	p=new CacheNode();//(CacheNode *)malloc(sizeof(CacheNode));
    	
    	if(p==NULL)
    		return false;
    
    	addToFront(p);
    	p->key=key;
    	p->value=value;
    		
    	cache_real_size_++;
    		
    	return true;	
    }
    
    
    void LRUCache::displayNodes()
    {
    	CacheNode *p=p_cache_list_head->next;
    	
    	while(p->next!=NULL)
    	{
    		cout << " Key : " << p->key << " Value : " << p->value << endl; 
    		p=p->next;
    		
    	}
    	cout << endl;
    	
    }


    说着后面的话


    其实,程序还可以优化,首先,把数据int类型换成模板形式的通用类型,另外,数据查找的时候复杂度为O(n),可以换成hash表来存数据,链表只做置换处理,这样查找添加的时候速度将快很多。





  • 相关阅读:
    JavaScrip中构造函数、prototype原型对象、实例对象三者之间的关系
    (字符缓冲流)文本排序案例
    Annotation注解的应用(打印异常信息)
    Annotation(注解)
    Java关键技术强化
    基本数据类型与引用数据类型的区别
    EKT反射
    bootstrap的概念
    Servlet强化
    java数据库连接池
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3303837.html
Copyright © 2011-2022 走看看