【leetcode】-146-LRU缓存机制-哈希表+双向链表

 

  • 数据结构如图所示: 哈希表和双向链表
    • 由于查找的时间复杂为o(1),所以只能用哈希表。
    • 由于插入删除移动的时间复杂度为o(1),所以只能用双向链表。

哈希表的key是结点值,value是一个指向双向链表的位置的指针,用双向链表才能在o(1)时间内移动或者删除结点。为了简便一些判断,在头部尾部都加入一个dummy结点。

  • 查找get(key)
    • 如果hash表中没有这个key值,返回-1
    • 如果有,通过哈希表的value值找到单链表中的某个结点,将该节点移动到链表表头,并返回结点值
  • 插入put(key,value)
    • 如果key值不存在
      • 在双向链表中创建一个新的结点,并在哈希表中创建一个键值对
      • 判断此时的节点数是否超出了容量
        • 如果超过容量,删除链表尾的结点,删除在哈希表中对应的项目,更新容量。
    • 如果key值存在
      • 通过哈希表定位结点,修改结点的值,并将该节点插入到表头。

太艰难了(加油!),注意事项如下

关于哈希表的操作不熟悉:

  1. 定义哈希表:unordered_map<int, DListNode*> HashMap;
  2. 判断哈希表是否为空:if (HashMap.count(key) == 0)return -1;
  3. 在哈希表中加入新的键值对:HashMap[key] = p;
  4. 根据哈希表的key值删除对应的表项:HashMap.erase(q->key);

对c++构造函数的语法不熟悉

  1. 使用初始化列表来初始化字段,冒号后面都是成员变量,在小括号里面直接赋值
    1. LRUCache(int _capacity) : capacity(_capacity), size(0){中间省略}
    2. DListNode() : key(0), value(0), front(nullptr), next(nullptr) {}
    3. DListNode(int _key, int _value) : key(_key), value(_value), front(nullptr),next(nullptr){}

对数据结构理解不透彻

  1. 双向链表的结点要同时存储key和value:存储value是因为get函数是由key查找value,虽然哈希表的value是一个指针,但是指针指向的双链表结点应该返回给用户一个value值。存储key是因为当容量过大时,除了移除双链表表尾结点之外,还要移除哈希表中对应的表项,erase需要获取key值。

对双链表的各种断链接链操作还是蛮熟悉的,表扬!

struct DListNode {
	int key,value;
	struct DListNode* front;
	struct DListNode* next;
	DListNode() : key(0), value(0), front(nullptr), next(nullptr) {}//在结构体中定义构造函数,这是不带参数的
	DListNode(int _key, int _value) : key(_key), value(_value), front(nullptr), next(nullptr){}//这是带参数的
};
class LRUCache {
private:
	DListNode* head;
	DListNode* tail;
	int size;
	int capacity;
	unordered_map<int, DListNode*> HashMap;
public:
	LRUCache(int _capacity) : capacity(_capacity), size(0) {//构造函数
		head = new DListNode();
		tail = new DListNode();
		head->next = tail;
		tail->front = head;
	}
	int get(int key) {
		if (HashMap.count(key) == 0)return -1;//查找失败返回-1
		DListNode* p=HashMap[key];
		moveTohead(p);
		return p->value;
	}

	void put(int key,int value) {
		if (HashMap.count(key) == 0) {//如果不存在
			//创建双链表结点
			DListNode* p = new DListNode(key,value);
			//插入双链表的表头
			insertTohead(p);
			//插入hash表
			HashMap[key] = p;

			++size;

			if (size > capacity) {	
				//如果容量不足,删除表尾		
				DListNode* q=deleteTail();
				//删除哈希表中对应的项
				HashMap.erase(q->key);
				//防止内存泄漏
				delete q;
				--size;
			}
		}
		else {
			//如果存在
			DListNode* p = HashMap[key];
			p->value = value;
			moveTohead(p);
		}
	}

	DListNode* deleteTail() {
		DListNode* p = tail->front;
		p->front->next = tail;
		tail->front = p->front;
		return p;
	}

	void insertTohead(DListNode* p){
		p->front = head;
		p->next = head->next;
		head->next->front=p;
		head->next = p;
	}

	void moveTohead(DListNode* p) {
		p->front->next = p->next;
		p->next->front = p->front;
		//把p结点取下来

		p->front = head;
		p->next = head->next;
		head->next->front = p;//p结点插入表头
		head->next = p;
	}
};

猜你喜欢

转载自blog.csdn.net/qq_39328436/article/details/113730187