leetcode 146. LRU 缓存机制

大耗子 2021年02月19日 15次浏览

题目:146. LRU 缓存机制

146. LRU 缓存机制

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制

实现 LRUCache 类:

  • LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:

输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

提示:

  • 1 <= capacity <= 3000
  • 0 <= key <= 3000
  • 0 <= value <= 104
  • 最多调用 3 * 104getput

解题思路

  • 使用unordered_map存储指针cache,方便查找。该结构是哈希表结构,查询效率是O(1)

  • 使用双链表结构方便数据的更新(插入,修改,删除)

  • get的时候查找cache即可得到指针。更新链表,并返回值即可。

  • put的时候查找cache是否存在不存在先判断是否有足够的容量,不够则删除末尾结点,然后将新数据添加到头结点,并删除缓存。 存在则更新结点数据,并更新结点再链表的位置。

代码

struct DLinkedNode {
	int key, value;
	DLinkedNode* prev;
	DLinkedNode* next;
	DLinkedNode() : key(0), value(0), prev(nullptr), next(nullptr) {}
	DLinkedNode(int _key, int _value) : key(_key), value(_value), prev(nullptr), next(nullptr) {}
};

class LRUCache {
private:
	unordered_map<int, DLinkedNode*> cache;
	int capacity;
	int size;
	DLinkedNode *head;
	DLinkedNode *tail;
public:
	LRUCache(int capacity) {
		this->capacity = capacity;
		size = 0;
		head = new DLinkedNode(); //假节点,方便插入与移除
		tail = new DLinkedNode();
		head->next = tail;
		tail->prev = head;
	}
	// O(1) ,因为存储结构是unordered_map,底层结构是哈希表
	int get(int key) {
		if (!cache.count(key)) {
			return -1;
		}
		moveHead(cache[key]);
		return cache[key]->value;
	}

	void put(int key, int value) {
		if (!cache.count(key)) {
			DLinkedNode *node = NULL;
			// 如果缓存满了,先删除缓存
			if (capacity <= size) {
				// 移出链表
				node = delTail();
				// 移出缓存
				cache.erase(node->key);
				delete node;
				node = NULL;
			}
			// 添加新结点
			node = new DLinkedNode(key, value);
			cache[key] = node; // 存入缓存中,方便读取
			addNode(node);
			size++;
			return ;
		}
		cache[key]->value = value;
		moveHead(cache[key]);
	}
	void addNode(DLinkedNode *node) {
		node->next = head->next;
		node->prev = head;
		head->next->prev = node;
		head->next = node;
	}
	// 只是移出链表,不delete是为了方便get时候更新链表
	void delNode(DLinkedNode *node) {
		node->prev->next = node->next;
		node->next->prev = node->prev;
	}
    // 移动结点到头部
	void moveHead(DLinkedNode *node) {
		delNode(node);
		addNode(node);
	}
	// 内存谁申请,谁管理,所以删除要放在put中
	DLinkedNode * delTail() {
		DLinkedNode *node = tail->prev;
		delNode(node);
		return node;
	}
};


int main()
{

	map<int, int> mtmp;
	LRUCache cache(10);
	LRUCache lRUCache(2);
	lRUCache.put(1, 1); // 缓存是 {1=1}
	lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
	lRUCache.get(1);    // 返回 1
	lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
	lRUCache.get(2);    // 返回 -1 (未找到)
	lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
	lRUCache.get(1);    // 返回 -1 (未找到)
	lRUCache.get(3);    // 返回 3
	lRUCache.get(4);    // 返回 4


}