LRU算法是关于内存页替换的一种有效算法。此处借用到地图数据管理中。
基本流程:定义内存中可以存储的地图块个数为16。
1,当加载的地图块个数<=16时,直接插入到list列表中;
2, 当地图块个数达到16后,再加载新的地图块,需要从list中删除最近最少使用的地图块,即list列表尾部的节点数据;
3,当需要查询的地图块在list中,将对应的地图块从list中移动到表头;
4,若查询的地图块不再list,则从底层数据库中加载,之后转步骤1,2.
该算法采用list和under_map实现。list中的节点为地图块(id,value),under_map存储地图块id和地图块的指针,方便实现O(1)查询。
以下是详细代码。
//最近最少使用算法,使用双向链表存储key-value数据,使用哈希表存储索引
#ifndef LRUCACHE_H
#define LRUCACHE_H
#include <unordered_map>
template<class Key,class Value>
//存储数据的节点
struct Node
{
//关键字
Key iKey;
//值
Value iValue;
//前后节点指针
Node* pParent;
Node* pNext;
Node():iKey(0),iValue(0),pParent(nullptr),pNext(nullptr)
{
}
Node(const Key& key, const Value& value):iKey(key),iValue(value),pParent(nullptr),pNext(nullptr)
{
}
~Node()
{
}
};
template<class Key,class Value>
class LRUCache
{
public:
//初始化函数
//nums: 内存中存储数据个数
LRUCache(int nums);
~LRUCache(void);
public:
//根据key获取值
Value get(const Key& key);
//插入值
void set(Key key,Value value);
void show();
private:
//将当前节点pNode从列表中移出
void remove(Node<Key,Value>* pNode);
void setHead(Node<Key,Value>* pNode);
void pop_back();
//清空所有数据
void clear();
private:
//数据总节点个数
int m_iNum;
//链表根节点。采用循环链表
Node<Key,Value>* m_pRoot;
//Node* m_pTail;
//哈希表,存储数据所在地址<key,Node*>
std::unordered_map<Key, Node<Key,Value>*> m_mapHash;
};
//初始化函数
//nums: 内存中存储数据个数
template<class Key,class Value>
LRUCache<Key,Value>::LRUCache(int nums):m_pRoot(nullptr)
{
m_iNum = nums;
m_pRoot = new Node<Key,Value> ();
//初始化时,根节点的父节点和子节点都指向本身
m_pRoot->pParent = m_pRoot;
m_pRoot->pNext = m_pRoot;
}
template<class Key,class Value>
void LRUCache<Key,Value>::clear()
{
std::unordered_map<int, Node<Key,Value> *>::iterator it = m_mapHash.begin();
for (;it != m_mapHash.end();++it)
{
delete it->second;
}
delete m_pRoot;
}
template<class Key,class Value>
LRUCache<Key,Value>::~LRUCache(void)
{
clear();
}
//根据key获取值
template<class Key,class Value>
Value LRUCache<Key,Value>::get(const Key& key)
{
std::unordered_map<int, Node<Key,Value> *>::iterator it = m_mapHash.find(key);
if (it == m_mapHash.end())
{
return -1;
}
Node<Key,Value> * pNode = it->second;
if (pNode != nullptr)
{
//从当前位置移除
remove(pNode);
//移动到头结点
setHead(pNode);
return pNode->iValue;
}
else
{
return -1;
}
}
//插入值
template<class Key,class Value>
void LRUCache<Key,Value>::set(Key key,Value value)
{
//查找是否已经存在
std::unordered_map<int, Node<Key,Value> *>::iterator it = m_mapHash.find(key);
//如果已经存在,则移动到头结点
if (it != m_mapHash.end())
{
//更新key对应的值
it->second->iValue = value;
//从列表中移出
remove(it->second);
//移动到头结点
setHead(it->second);
}
else
{
//否则,如果已满,则删除尾结点
if (m_mapHash.size()>= size_t(m_iNum))
{
pop_back();
}
std::cout<<"插入新节点("<<key<<","<<value<<")"<<std::endl;
//插入新的节点作为头结点
Node<Key,Value> * pNewNode = new Node<Key,Value>(key,value);
setHead(pNewNode);
//在hashMap中添加对应索引
m_mapHash.insert(std::make_pair(key,pNewNode));
}
}
//将当前节点pNode从列表中移出
template<class Key,class Value>
void LRUCache<Key,Value>::remove(Node<Key,Value>* pNode)
{
if(pNode==nullptr)
{
return;
}
else
{
//如果当前节点是头结点
if(pNode->pParent == m_pRoot)
{
return;
}
//把当前节点的子节点付给当前节点父节点子节点
pNode->pParent->pNext = pNode->pNext;
//把当前节点的父节点赋值给当前节点子节点的父节点
pNode->pNext->pParent = pNode->pParent;
}
}
//插入到头结点
template<class Key,class Value>
void LRUCache<Key,Value>::setHead(Node<Key,Value>* pNode)
{
if (pNode==nullptr)
{
return;
}
else
{
//如果是头结点,直接返回
if (pNode->pParent == m_pRoot)
{
return;
}
else
{
std::cout<<"移动到头结点("<<pNode->iKey<<","<<pNode->iValue<<")"<<std::endl;
//旧的头结点赋值给当前节点在子节点
pNode->pNext = m_pRoot->pNext;
//旧的头结点的父节点赋值给当前节点的父节点
pNode->pParent = m_pRoot->pNext->pParent;
//设置旧节点的父节点为当前节点
m_pRoot->pNext->pParent = pNode;
//根节点的子节点为新的头节点
m_pRoot->pNext = pNode;
}
}
}
//删除尾结点
template<class Key,class Value>
void LRUCache<Key,Value>::pop_back()
{
//m_pRoot->pParent为尾结点
if (m_mapHash.size()==0)
{
return;
}
else
{
//获取尾结点
Node<Key,Value>* pTail = m_pRoot->pParent;
std::cout<<"移除尾结点("<<pTail->iKey<<","<<pTail->iValue<<")"<<std::endl;
//从hash表中删除尾结点的索引
m_mapHash.erase(pTail->iKey);
//尾结点的头结点的子节点为根节点
pTail->pParent->pNext = m_pRoot;
//新的尾结点为旧尾结点的父节点
m_pRoot->pParent = pTail->pParent;
}
}
template<class Key,class Value>
void LRUCache<Key,Value>::show()
{
//获取头结点
Node<Key,Value>* pNode = m_pRoot->pNext;
while(pNode != m_pRoot)
{
std::cout<<"Key-Value("<<pNode->iKey<<","<<pNode->iValue<<")"<<std::endl;
pNode = pNode->pNext;
}
std::cout<<std::endl;
}
#endif //LRUCACHE_H
// LRUAlg.cpp : 定义控制台应用程序的入口点。
//
#include "LRUCache.h"
#include <iostream>
int main(int argc, char* argv[])
{
LRUCache<int,int>* pCach = new LRUCache<int,int>(5);
for (int i=0;i<=10;++i)
{
pCach->set(i,i);
pCach->show();
}
int iValue = 0;
iValue = pCach->get(7);
std::cout<<"获取的值为:"<<iValue<<std::endl;
/*pCach->set(1,1);
pCach->set(2,2);
pCach->show();
iValue = pCach->get(2);
std::cout<<"获取的值为:"<<iValue<<std::endl;
pCach->set(3,3);*/
pCach->show();
iValue= pCach->get(6);
std::cout<<"获取的值为:"<<iValue<<std::endl;
pCach->show();
iValue= pCach->get(8);
std::cout<<"获取的值为:"<<iValue<<std::endl;
pCach->show();
return 0;
}