题目链接
https://oj.leetcode.com/problems/lru-cache/
题目让设计一个LRUCache,即根据LRU算法设计一个缓存。
在这之前需要弄清楚LRU算法的核心思想,LRU全称是LeastRecentlyUsed,即最近最久未使用的意思。
在操作系统的内存管理中,有一类很重要的算法就是内存页面置换算法(包括FIFO,LRU,LFU等几种常见页面置换算法)。
事实上,Cache算法和内存页面置换算法的核心思想是一样的:都是在给定一个限定大小的空间的前提下,
设计一个原则如何来更新和访问其中的元素。下面说一下LRU算法的核心思想,
LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。
也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。
1.注意点有两个:用什么结构去存储
2.查找,删除时间问题。
下面是AC的代码。
存储用的是map结构,便于查找,如果使用hash_map会更快些,但是测试编译不通过。
主键key,只是list双向链表的一个iterator,关于双链表,有个能在O(1)的复杂度度内删除节点的文章,讲的很详细。
class LRUCache{
public:
LRUCache(int capacity) {
this->capacity = capacity;
}
int get(int key) {
int reVal = -1;
map<int,list< pair<int,int> >::iterator>::iterator it = m_map.find(key);
// 如果找到,就把这个键值移动到最前面,并返回
if(it != m_map.end())
{
reVal = (it->second)->second;
m_list.erase(it->second);
m_list.push_front(*(it->second));
m_map[it->first] = m_list.begin();
}
return reVal;
}
void set(int key, int value) {
map<int,list< pair<int,int> >::iterator>::iterator it = m_map.find(key);
if(it != m_map.end())
{
m_list.erase(it->second);
m_map.erase(it->first);
pair<int, int> temp = make_pair(key, value);
m_list.push_front(temp);
m_map[key] = m_list.begin();
}
else
{
if(capacity > m_list.size())
{
pair<int, int> temp = make_pair(key, value);
m_list.push_front(temp);
m_map[key] = m_list.begin();
}
else
{
pair<int, int> temp = m_list.back();
m_list.pop_back();
m_map.erase(temp.first);
temp = make_pair(key, value);
m_list.push_front(temp);
m_map[key] = m_list.begin();
}
}
}
unsigned capacity;
map<int,list< pair<int,int> >::iterator> m_map; //对应的map
list< pair<int,int> > m_list; //对应的list
};
转载:
在O(1)时间删除链表结点
http://www.cnblogs.com/xwdreamer/archive/2012/04/26/2472102.html
给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下:
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
};
void DeleteNode(ListNode** pListHead,ListNode* pToBeDeleted);
删除结点的操作我们经常碰到,比如一个链表A->B->C->D->E->F->G。如果我们要删除结点E,
那么我们只需要让结点D的指针指向结点F即可,但是我们现在只给出链表头结点的指针以及结点E的指针,
而又是单项链表,不能在O(1)时间内得到被删除结点前面的那一个结点的指针,
所以我们原先的方法是不能在O(1)时间内删除结点E的。
那么既然我们不能获得被删除结点的前一个结点的指针,我们就需要转变思路来考虑是否能够用过被删除
结点后一个结点的指针来解决方法。因此被删除结点E的后一个结点指针是很容易得到的,也就是E->m_pNext。
那么我们可能会想到如下方法:获得F的指针,将F的数据赋值给E,然后让E指向F的下一个结点。
这里虽然删除的是结点F,但是相当于删除的是结点E。并且是O(1)时间复杂度。下面给出代码实例:
#include<iostream>
#include<stdlib.h>
#include<stack>
using namespace std;
//链表结构
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
};
//创建一个链表结点
ListNode* CreateListNode(int value)
{
ListNode *pNode=new ListNode();
pNode->m_nValue=value;
pNode->m_pNext=NULL;
return pNode;
}
//遍历链表中的所有结点
void PrintList(ListNode* pHead)
{
ListNode *pNode=pHead;
while(pNode!=NULL)
{
cout<<pNode->m_nValue<<" ";
pNode=pNode->m_pNext;
}
cout<<endl;
}
void ConnectListNode(ListNode* pCurrent,ListNode* pNext)//连接两个结点
{
if(pCurrent==NULL)
{
cout<<"前一个结点不能为空"<<endl;
exit(1);
}
else
{
pCurrent->m_pNext=pNext;
}
}
void DeleteNode(ListNode** pListHead,ListNode* pToBeDeleted)
{
if(pToBeDeleted->m_pNext!=NULL)//如果要删除结点后面还有结点
{
ListNode* pNext=pToBeDeleted->m_pNext;
pToBeDeleted->m_nValue=pNext->m_nValue;
pToBeDeleted->m_pNext=pNext->m_pNext;
delete pNext;
pNext=NULL;
}
else if(*pListHead==pToBeDeleted)//如果链表只有一个结点
{
delete pToBeDeleted;
pToBeDeleted=NULL;
*pListHead=NULL;
}
else//如果链表有多个结点,且删除最后一个结点,那么只能遍历链表
{
ListNode *pNode=*pListHead;
while(pNode->m_pNext!=pToBeDeleted)
pNode=pNode->m_pNext;
pNode->m_pNext=NULL;
delete pToBeDeleted;
pToBeDeleted=NULL;
}
}
void main()
{
//创建结点
ListNode* pNode1=CreateListNode(1);//创建一个结点
ListNode* pNode2=CreateListNode(2);//创建一个结点
ListNode* pNode3=CreateListNode(3);//创建一个结点
ListNode* pNode4=CreateListNode(4);//创建一个结点
ListNode* pNode5=CreateListNode(5);//创建一个结点
ListNode* pNode6=CreateListNode(6);//创建一个结点
ListNode* pNode7=CreateListNode(7);//创建一个结点
//连接结点
ConnectListNode(pNode1,pNode2);//连接两个结点
ConnectListNode(pNode2,pNode3);//连接两个结点
ConnectListNode(pNode3,pNode4);//连接两个结点
ConnectListNode(pNode4,pNode5);//连接两个结点
ConnectListNode(pNode5,pNode6);//连接两个结点
ConnectListNode(pNode6,pNode7);//连接两个结点
//打印链表
PrintList(pNode1);//打印
//删除结点
DeleteNode(&pNode1,pNode3);
//打印链表
PrintList(pNode1);//打印
system("pause");
}