代码实现LRU最近很少使用算法

17 篇文章 1 订阅

LRU全称(Least Recently Used), 称为最近很少使用算法.
意思是, 根据最近访问的记录, 对于缓存的数据进行淘汰, 如果一个数据最近被访问, 或者经常被访问, 那么它就会处于列表前面的位置(比如数组或者链表的前面), 而一个数据很长时间没有被访问, 就会处于靠后的位置, 这个时候缓存如果满了, 来了新的数据, 就会把处于尾部的数据淘汰掉, 并将新的数据放入列表.

LRU算法通常是用一个哈希表和一个双向链表实现的
其实就如我们之前在概念中提到的那样, 也可以是一个数组, 也可以是一个列表.
当我们使用数组的时候, 查询效率较高, 当不方便在头部插入数据, 因为当缓存满了又到来新数据的时候, 我们往往会将其放在列表的头部, 表示它是最近被访问(这样可以使得处于列表末尾的数据就是最近很少访问, 或最近很少使用).
如果选择使用链表, 这个时候头部插入效率很高, 但查询的效率又变低了, 因为它不支持随机访问.
综合以上考虑, 我们选择使用哈希+双向链表的方式来实现LRU, 这样通过哈希映射我们能很快确定一个key值是否存在, 并且可以很快完成一个节点的移动和头部插入.

关于get方法
如果查询key值不存在, 直接返回-1;
如果存在, 则返回key值所对应的最近被使用的节点.

关于put方法
如果key不存在, 我们就需要构建一个新的节点, 并将该节点添加至链表头部, 表示最近被使用;
如果key存在, 就通过哈希映射, 找到对应节点, 并修改对应节点中的value值, 并将其移动值链表头部

代码示例:

struct DLinkListNode
{
	int _key;
	int _val;
	DLinkListNode* _next;
	DLinkListNode* _prev;

	//默认构造函数
	DLinkListNode() :
		_key(0),
		_val(0),
		_next(nullptr),
		_prev(nullptr)
	{}

	//带参构造
	DLinkListNode(const int key, const int val) :
		_key(key),
		_val(val),
		_next(nullptr),
		_prev(nullptr)
	{}
};

class LRU
{
public:
	LRU(int capacity) :
		_size(0),
		_capacity(capacity)
	{
		_head = new DLinkListNode();
		_tail = new DLinkListNode();

		_head->_next = _tail;
		_tail->_prev = _head;
	}

	int get(const int key)
	{
		//如果不在哈希表中, 返回-1
		if (!m.count(key))
			return -1;

		//如果在哈希表中, 将该节点移至头部
		DLinkListNode* cur = m[key];

		moveToHead(cur);
		return cur->_val;
	}

	void put(const int key, const int val)
	{
		if (!m.count(key))
		{
			DLinkListNode* cur = new DLinkListNode(key, val);//不在就创建一个新节点

			//添加进哈希表
			m[key] = cur;

			addNodeToHead(cur);//并将其移至头部
			++_size;
			if (_size > _capacity)
			{
				//说明此时超出容量, 要移除双向链表尾部的节点(表示最久未使用的数据)
				DLinkListNode* remove_node = removeTailNode();

				//同时还要删除哈希表中对用的项
				m.erase(remove_node->_key);

				delete remove_node;//在removeTailNode()函数中只是将要删除的节点架空,并没有释放空间
				--_size;
			}
		}
		else
		{
			//key存在, 通过哈希表定位
			DLinkListNode* cur = m[key];

			//记得修改value的值
			cur->_val = val;
			//再将其移至链表头部
			moveToHead(cur);
		}
	}
	
	DLinkListNode* removeTailNode()
	{
		DLinkListNode* tail = _tail->_prev;
		removeNode(tail);
		return tail;
	}

	void removeNode(DLinkListNode* node)
	{
		node->_next->_prev = node->_prev;
		node->_prev->_next = node->_next;
	}

	void addNodeToHead(DLinkListNode* node)
	{
		node->_next = _head->_next;
		node->_prev = _head;
		
		_head->_next->_prev = node;
		_head->_next = node;
	}

	void moveToHead(DLinkListNode* node)
	{
		//删除当前节点
		removeNode(node);

		//将当前节点添加至头部
		addNodeToHead(node);
	}
	
private:
	std::unordered_map<int, DLinkListNode*> m;
	DLinkListNode* _head;
	DLinkListNode* _tail;

	int _size;
	int _capacity;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值