[leetcode]LRU Cache 值得思考的一道题

题目链接
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");

}



你可能感兴趣的:([leetcode]LRU Cache 值得思考的一道题)