算法

LRU 缓存机制可以通过哈希表辅以双向链表实现,用一个哈希表和一个双向链表维护所有在缓存中的键值对。

  • 双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
  • 哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。
  • 当插入时将节点插入靠近头部的位置,并存储到map中,如果到达容量上限则移除靠近链表尾部的节点同时在map中删除该键值对
  • 当获取时返回节点值并将节点移动到靠近链表头部的位置

LRU缓存C++简单实现

// LRU缓存C++简单实现
#include <unordered_map>

/**节点类(构建双向链表)*/
class DLinkedNode {
	int key, value;
	DLinkedNode* prev;
	DLinkedNode* next;
	DLinkedNode(int key_, int value_) : key(key_), value(value_), prev(nullptr), next(nullptr) {}

	friend class LRUCache;
};

/**缓存类*/
class LRUCache {
public:
	LRUCache(int capacity_) :capacity(capacity_), size(0) {
		//为方便操作的伪头部
		head = new DLinkedNode(0, 0);
		//为方便操作的伪尾部
		tail = new DLinkedNode(0, 0);
		head->next = tail;
		tail->prev = head;
		//头尾都不实际加入cache中
	}

	~LRUCache() {
		delete head;
		delete tail;
	}

	int get(int key) {
		if (!cache.count(key)) {
			return -1;//key不存在
		}
		// 如果 key 存在,先通过哈希表定位,再移到头部
		DLinkedNode* node = cache[key];
		moveToHead(node);
		return node->value;
	}

	void put(int key, int value) {
		if (!cache.count(key)) {
			// 如果 key 不存在,创建一个新的节点
			DLinkedNode* node = new DLinkedNode(key, value);
			// 添加进哈希表
			cache[key] = node;
			// 添加至双向链表的头部
			addToHead(node);
			++size;
			if (size > capacity) {
				// 如果超出容量,删除双向链表的尾部节点
				DLinkedNode* removed = removeTail();
				// 删除哈希表中对应的项
				cache.erase(removed->key);
				// 删除对象
				delete removed;
				--size;
			}
		}
		else {
			// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
			DLinkedNode* node = cache[key];
			node->value = value;
			moveToHead(node);
		}
	}

private:
	std::unordered_map<int, DLinkedNode*> cache;
	DLinkedNode* head;
	DLinkedNode* tail;
	int size, capacity;

	void addToHead(DLinkedNode* node) {
		node->prev = head;
		node->next = head->next;
		head->next->prev = node;
		head->next = node;
	}

	void removeNode(DLinkedNode* node) {
		node->prev->next = node->next;
		node->next->prev = node->prev;
	}

	void moveToHead(DLinkedNode* node) {
		removeNode(node);
		addToHead(node);
	}

	DLinkedNode* removeTail() {
		DLinkedNode* node = tail->prev;
		removeNode(node);
		return node;
	}
};

LRU缓存Rust简单实现

//LRU缓存Rust简单实现
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;

struct Node {
    key: i32, value: i32,
    prev: Option<Rc<RefCell<Node>>>,
    next: Option<Rc<RefCell<Node>>>
}

impl Node {
    fn new(key: i32, value: i32) -> Self {
        Node {
            key, value,
            prev: None, next: None
        }
    }
}

struct LRUCache {
    //hash缓存,便于查找
    cache: HashMap<i32,Rc<RefCell<Node>>>,
    //伪头尾,便于操作节点时不用关心其前后节点是否存在
    head: Rc<RefCell<Node>>, tail: Rc<RefCell<Node>>,
    //大小<=容量
    size: usize, capacity: usize
}

impl LRUCache {
    fn new(capacity: i32) -> Self {
        let capacity = match capacity < 1 {
            true => 1 as usize,
            false => capacity as usize
        };
        let head = Rc::from(RefCell::from(Node::new(-1,-1)));
        let tail = Rc::new(RefCell::new(Node::new(-2,-2)));
        head.borrow_mut().next = Option::from(tail.clone());
        tail.borrow_mut().prev = Some(head.clone());
        Self {
            cache: HashMap::with_capacity(capacity),
            head, tail,
            size: 0 as usize, capacity,
        }
    }

    ///以key获取value
    fn get(&mut self, key: i32) -> i32 {
        if let Some(node_ref) = self.cache.get(&key) {
            self.move_to_head(node_ref);
            return node_ref.borrow().value;
        }
        -1
    }

    ///插入key-value
    fn put(&mut self, key: i32, value: i32) {
        match self.cache.get(&key) {
            // 如果 key 不存在,创建一个新的节点
            None => {
                //到达容量上限,则先清除最老的数据
                if self.size == self.capacity {
                    self.size -= 1;
                    if let Some(node) = self.remove_tail() {
                        self.cache.remove(&node.borrow().key);
                    }
                }
                self.size += 1;
                let node = Rc::new(RefCell::new(Node::new(key,value)));
                self.cache.insert(key,node.clone());
                self.move_to_head(&node);
            }
            // 存在则移动到头部并改写其值
            Some(node_ref) => {
                node_ref.borrow_mut().value = value;
                self.move_to_head(node_ref);
            }
        }
    }

    fn move_to_head(&self, node: &Rc<RefCell<Node>>){
        Self::remove_node(node);
        self.add_to_head(node);
    }

    ///将节点添加到头部
    fn add_to_head(&self, node: &Rc<RefCell<Node>>) {
        //当前节点的prev指向head
        node.borrow_mut().prev.replace(self.head.clone());
        //原头部节点
        if let Some(head_next_ref) = &self.head.borrow().next {
            //当前节点的next指向head的next,完成自身移动到头部
            node.borrow_mut().next.replace(head_next_ref.clone());
            //原来头节点next的prev指向当前节点
            head_next_ref.borrow_mut().prev.replace(node.clone());
        }
        //原来头节点的next指向将当前节点,完成原来的头部后移为第二位
        self.head.borrow_mut().next.replace(node.clone());
    }

    ///从链表中删除尾节点,并返回该节点
    fn remove_tail(&mut self) -> Option<Rc<RefCell<Node>>> {
        if let Some(node) = &self.tail.borrow().prev {
            let node = Self::remove_node(&node);
            return Some(node.clone());
        }
        None
    }

    ///由于需要同时持有node的可变借用,使用unsafe
    unsafe fn remove_node_unsafe(node: &Rc<RefCell<Node>>) -> Rc<RefCell<Node>>{
        if let Some(prev) = &node.borrow().prev {
            if let Some(next) = &node.borrow().next {
                //将前一节点的后置节点指向当前节点的后置节点
                prev.borrow_mut().next.replace(next.clone());
                //预先克隆并返回克隆值,避免返回当前节点处在尾部时下一步操作将其内部Node改变为操作后的尾部
                let node = node.clone();
                //将后一节点的前置节点指向当前节点的前置节点,当移除最老节点时这个操作对于tail而言改变了尾部节点
                (*next.as_ptr()).prev = Some(prev.clone());//这里的裸指针就是使用usafe的原因(next.borrow_mut()将报错:already borrowed: BorrowMutError)
                //(*node.as_ptr()).prev = None;
                //(*node.as_ptr()).next = None;
                return node;
            }
        }
        node.clone()
    }

    //将节点从链表中移除
    fn remove_node(node: &Rc<RefCell<Node>>)  -> Rc<RefCell<Node>>{
        unsafe {
            return Self::remove_node_unsafe(node);
        }

    }
}

///测试
fn main() {
    let mut cache = LRUCache::new(2);
    cache.put(1,11);
    cache.put(2,22);
    println!("get 1 {}  1",cache.get(1));
    cache.put(3,33);
    println!("get 2 {}  -1",cache.get(2));
    cache.put(4,44);
    cache.put(3,333);
    println!("get 1 {}  -1",cache.get(1));
    println!("get 3 {}  3",cache.get(3));
    println!("get 4 {}  4",cache.get(4));
}
/*
get 1 11  1
get 2 -1  -1
get 1 -1  -1
get 3 333  3
get 4 44  4
*/

C++循环链表实现

// LRU缓存C++简单实现(循环链表+哈希表)
#include <unordered_map>

/**节点类(构建循环双向链表)*/
class DLinkedNode {
	int key, value;
	DLinkedNode* prev;
	DLinkedNode* next;
	DLinkedNode(int key_, int value_) : key(key_), value(value_), prev(nullptr), next(nullptr) {}

	friend class LRUCache;
};

/**缓存类*/
class LRUCache {
public:
	LRUCache(int capacity_) :capacity(capacity_), size(0) {
		//初始化标记节点,也可以直接用put的首节点做标记节点,代价是每次put都要判空
		mark = new DLinkedNode(0, 0);
		mark->next = mark;
		mark->prev = mark;
		//初始化标记不实际加入cache中
	}

	~LRUCache() {
		delete mark;
	}

	int get(int key) {
		if (!cache.count(key)) {
			return -1;//key不存在
		}
		// 如果 key 存在,先通过哈希表定位,再移到头部
		DLinkedNode* node = cache[key];
		moveToHead(node);
		return node->value;
	}

	void put(int key, int value) {
		if (!cache.count(key)) {
			removeTailBeforePut();
			// 如果 key 不存在,创建一个新的节点
			DLinkedNode* node = new DLinkedNode(key, value);
			// 添加进哈希表
			cache[key] = node;
			// 添加至双向链表的头部
			addToHead(node);
			++size;
		}
		else {
			// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
			DLinkedNode* node = cache[key];
			node->value = value;
			moveToHead(node);
		}
	}

private:
	std::unordered_map<int, DLinkedNode*> cache;
	//标记节点,他的next是首节点,prev是尾节点
	DLinkedNode* mark;
	int size, capacity;

	void removeTailBeforePut() {
		if (size >= capacity) {
			// 如果超出容量,删除双向链表的尾部节点
			DLinkedNode* removed = removeTail();
			// 删除哈希表中对应的项
			cache.erase(removed->key);
			// 删除对象
			delete removed;
			--size;
			removeTailBeforePut();
		}
	}

	void addToHead(DLinkedNode* node) {
		DLinkedNode* lastHead = mark->next;
		node->prev = mark;
		node->next = lastHead;
		mark->next->prev = node;
		mark->next = node;
	}

	void removeNode(DLinkedNode* node) {
		node->prev->next = node->next;
		node->next->prev = node->prev;
	}

	void moveToHead(DLinkedNode* node) {
		removeNode(node);
		addToHead(node);
	}

	DLinkedNode* removeTail() {
		DLinkedNode* node = mark->prev;
		removeNode(node);
		return node;
	}
};

Rust循环链表实现

// LRU缓存Rust简单实现(循环链表+哈希表)
use std::collections::HashMap;
use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;

struct ListNode {
    key: i32,
    val: i32,
    prev: Option<Weak<RefCell<ListNode>>>,//弱引用以避免使用unsafe
    next: Option<Rc<RefCell<ListNode>>>,
}

impl ListNode {

    #[inline]
    fn new(key:i32, val: i32) -> Self {
        ListNode{
            key,val,
            prev: None,
            next: None,
        }
    }

    #[inline]
    fn new_rc(key:i32, val: i32) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(Self::new(key,val)))
    }

    fn insert_after(node: Rc<RefCell<Self>>, head: Rc<RefCell<Self>>) {
        let mut next = head.borrow_mut().next.take();
        next.as_mut().unwrap().borrow_mut().prev = Some(Rc::downgrade(&node));
        node.borrow_mut().next = next;
        node.borrow_mut().prev = Some(Rc::downgrade(&head));
        head.borrow_mut().next = Some(node);
    }

    fn pop(node: Rc<RefCell<Self>>) -> Rc<RefCell<Self>> {
        let next = node.borrow_mut().next.take().unwrap();
        let prev = node.borrow_mut().prev.as_ref().unwrap().upgrade().unwrap();
        prev.borrow_mut().next = Some(Rc::clone(&next));
        next.borrow_mut().prev = Some(Rc::downgrade(&prev));
        node.borrow_mut().prev = None;
        node
    }

}

struct LRUCache {
    map: HashMap<i32, Rc<RefCell<ListNode>>>,
    //标记节点,他的next是首节点,prev是尾节点
    mark: Rc<RefCell<ListNode>>,
    capacity: usize,
}

impl LRUCache {

    fn new(capacity: i32) -> Self {
        let capacity= match capacity.le(&0) {
            true => 1 as usize,
            false => capacity as usize
        };
        let mark = ListNode::new_rc(0, 0);
        mark.borrow_mut().next = Some(Rc::clone(&mark));
        mark.borrow_mut().prev = Some(Rc::downgrade(&mark));

        LRUCache {map: HashMap::with_capacity(capacity),mark,capacity}
    }

    #[inline]
    fn get(&self, key: i32) -> i32 {
        self.get_node(key).0
    }

    fn get_node(&self, key: i32) -> (i32, Option<Rc<RefCell<ListNode>>>) {
        match self.map.get(&key) {
            None => {
                (-1, None)
            }
            Some(x) => {
                let node_rc = ListNode::pop(Rc::clone(x));
                let ret = node_rc.borrow().val;
                let ret_node = Some(Rc::clone(&node_rc));
                self.insert_after_head(node_rc);
                (ret, ret_node)
            }
        }
    }

    fn put(&mut self, key: i32, value: i32) {
        let (ret, ret_node) = self.get_node(key);
        if  ret != -1 {
            ret_node.unwrap().borrow_mut().val = value;
        } else{
            self.rm_surplus_tail_before_put();
            let new = ListNode::new_rc(key, value);
            self.map.insert(key, new.clone());
            self.insert_after_head(new);
        }
    }

    #[inline]
    fn insert_after_head(&self,new: Rc<RefCell<ListNode>>) {
        ListNode::insert_after(new, Rc::clone(&self.mark));
    }

    fn rm_surplus_tail_before_put(&mut self) {
        if self.map.len().ge(&(self.capacity as usize)) {
            let to_rm = Rc::clone(&self.mark.as_ref().borrow().prev.as_ref().unwrap().upgrade().unwrap());
            let to_rm = ListNode::pop(to_rm);
            self.map.remove(&to_rm.borrow().key);
            self.rm_surplus_tail_before_put();
        }
    }
}
03-08 17:40