1.背景:为什么用双向链表管理LRU?为什么不用单链表?
由于LRU算法的特性,需要不断的对链表进行添加以及移位的操作(添加新入元素,移位:最近访问的元素加到链表头,最近访问的元素要移动到链表尾),所以需要维护链表的头节点以及尾节点位置,如果用单链表实现,效率非常低,因为每次要添加元素都要遍历链表找到链表尾(O(n))。所以用双向链表的优势,可以只用一个head头节点就可以用O(1)复杂度查找到链表头与链表尾的位置。
例如:(放最近访问的元素)<-----prev head next------>(放新添加的元素)
接下来从新建LRU链表,添加元素, 移位元素(即最近访问的元素移位)来介绍此算法
2.新建LRU链表
// 链表结构体
struct LinkNode{
int value;
LinkNode* prev;
LinkNode* next;
LinkNode(int _value = 0, LinkNode* p = nullptr, LinkNode* n = nullptr):
value(_value), prev(p), next(n) {}
};
// 初始化头节点
LinkNode head;
head.prev = &head;
head.next = &head;
3.向LRU链表中添加元素
std::vector<int> Test{1,3,5,5,1,4,6,4,3,2,1,2,3,4,1,2,3,1,5,,5,5,5,2,1};
LinkNode* curr;
for (auto i:Test) {
curr = new LinkNode(i);
// 新加入的节点,全部放在head的next方向的第一个,也就是prev方向的最后一个
// <-----prev head next------>
curr->next = head.next;
curr->prev = &head;
head.next->prev = curr;
head.next = curr;
}
4.对LRU链表操作,移动最近访问的元素到prev方向首位
// 需要的操作: 把curr节点放到head的prev方向的第一位
// 1.删除节点:
curr->prev->next = curr->next;
curr->next->prev = curr->prev;
// 2.插入该位置:
// 最近访问的节点,全部放在head的prev方向的第一个,也就是next方向的最后一个
// <-----prev head next------>
curr->prev = head.prev;
curr->next = &head;
head.prev->next = curr;
head.prev = curr;