赞
踩
做LeetCode第146题LRU缓存,觉得收获不小,特此记录。
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类:
- LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存。
- int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
- void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该逐出最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
关于LRU的规则,学过操作系统的肯定都知道,此处不再赘述。
题目关键在于 get() 和 put() 必须O(1),这就使得我们选择数据结构的时候需要有些考究。
首先我们回顾一下本科学过的数据结构知识,存储这样的多个key-value对肯定是使用线性表存储,而线性表又分为顺序表(数组)和链表,由于题目中涉及到了插入和删除(逐出),且有O(1)的条件限制,所以肯定是使用链表来存储。
确定了基本数据结构,我们再来想想如何使用或改造使其满足题目需要。
首先考虑 get(),我们需要做到如果关键字key在缓存中,就能查找到关键字的值,且有O(1)的条件限制,那自然是使用哈希表,哈希表的键就是LRU缓存中的key自然不用说,关键在于 哈希表的值是什么?直观想法那就是LRU的value呗,其实不然,因为我们其实不仅要通过key查到value,还有一个隐含的操作,即需要把我们刚刚查过的这个LRU缓存节点置于最高优先级的位置上(LRU的规则),所以该哈希表的value应该是一个链表节点,我们通过key查到这个链表节点以后,就可以对其进行操作以改变其优先级,同时我们也可以直接通过node->value来取得key对应的value值,一举两得。
那到底怎么体现优先级,怎么做到关键字的插入和逐出呢,其实很简单,在链表头部的表示优先级最高(即最近使用),在链表尾部的表示优先级最低(即最久未使用),这样一来 put() 的编写就容易了,当来一个新关键字的时候,就把其节点插入链表头,同时更新哈希表。每当链表中的节点个数超过LRU的容量时,我们就删除掉链表尾的的元素,同时更新哈希表。
现在我们还有一个问题需要解决,就是使用普通的单链表,是否能满足put()和get()均为O(1)的题目要求。根据以上的分析,我们对链表会有以下操作:
根据以上对链表的要求,可以确定只有双链表能满足要求,单链表、单循环链表、带尾指针的单循环链表都无法在O(1)的时间下做到以上三个操作,大家可以模拟一下。
确定好数据结构,代码就很好写了,只需按需实现LRU功能即可:
struct doubleLinkNode { int key, value; doubleLinkNode *pre, *next; }; class LRUCache { private: doubleLinkNode *head, *rear; int size, capacity; unordered_map<int, doubleLinkNode*> map; void insert(doubleLinkNode* node) { // 把一个非链表上的节点插入到表头 node->pre = head; node->next = head->next; head->next->pre = node; head->next = node; } void insertHead(doubleLinkNode* node) { // 把一个链表上的节点插入到表头 node->pre->next = node->next; node->next->pre = node->pre; insert(node); } int deleteRear() { doubleLinkNode *node = rear->pre; node->pre->next = node->next; node->next->pre = node->pre; int key = node->key; delete node; return key; } public: LRUCache(int cap): size(0), capacity(cap) { head = new doubleLinkNode(); rear = new doubleLinkNode(); head->next = rear; head->pre = nullptr; rear->next = nullptr; rear->pre = head; } int get(int key) { if(map.count(key)) { insertHead(map[key]); return map[key]->value; } return -1; } void put(int key, int value) { if(map.count(key)) { doubleLinkNode *node = map[key]; if(node->value != value) { node->value = value; } insertHead(node); } else { doubleLinkNode *node = new doubleLinkNode; node->key = key; node->value = value; insert(node); ++size; if(size > capacity) { int nodeKey = deleteRear(); map.erase(nodeKey); --size; } map[key] = node; } } };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。