题目链接
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 };
转载:
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"); }