146LRU缓存机制(哈希表+list)

本文详细介绍了如何使用HashMap和List实现LRU(最近最少使用)缓存机制,包括数据结构设计、时间复杂度分析及代码示例。通过具体示例展示了LRU缓存的get和put操作,以及如何在O(1)时间内完成这些操作。

1、题目描述

运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥已经存在,则变更其数据值;如果密钥不存在,则插入该组「密钥/数据值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

进阶:

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

2、示例

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

3、题解

基本思想:HashMap+list,时间复杂度O(1),好的数据结构是成功的一半。

list保存key->value对,从头到尾按照最近使用(头)到最少使用(尾)顺序排列。

HashMap保存key->iterator对,iterator指向list中保存相应key的对,通过HashMap[key]可以快速找到key在list中位置,方便查找删除插入操作,注意:链表的插入删除操作迭代器不会失效。

  • 进阶一:手动实现list,自定义结构体MyListNode,LRU保存head、tail。
  • 进阶二:手动实现list和map,自定义实现MyListNode和MyHashMap。
#include<iostream>
#include<algorithm>
#include<vector>
#include<unordered_map>
using namespace std;
//利用list和map数据结构实现
class LRUCache {
public:
    LRUCache(int capacity) : cap(capacity) {
    }
    int get(int key) {
        if (map.find(key) == map.end()) return -1;
        auto key_value = *map[key];
        cache.erase(map[key]);
        cache.push_front(key_value);
        map[key] = cache.begin();
        return key_value.second;
    }

    void put(int key, int value) {
        if (map.find(key) == map.end()) {
            if (map.size() == cap) {
                map.erase(cache.back().first);
                cache.pop_back();
            }
        }
        else
            cache.erase(map[key]);

        cache.push_front({key, value});
        map[key] = cache.begin();
    }
private:
    int cap;
    list<pair<int, int>> cache;
    unordered_map<int, list<pair<int, int>>::iterator> map;
};
struct MyListNode {
  int key;
  int value;
  MyListNode *next;
  MyListNode *pre;
  MyListNode():key(0),value(0),next(nullptr),pre(nullptr) {}
  MyListNode(int key_,int value_,MyListNode *next_,MyListNode *pre_):key(key_),value(value_),next(next_),pre(pre_) {}
};
//进阶一:手动实现list
class LRUCache {
public:
  LRUCache(int capacity) : cap(capacity)
  {
    head = new MyListNode();
    tail = head;
  }
  virtual ~LRUCache() {}
  int get(int key) {
    if(lru_map.find(key)!=lru_map.end()){
      MyListNode *cur=lru_map[key];
      int value=cur->value;
      erase_list_node(cur);
      insert_front_list(cur);
      lru_map[key]=cur;
      return value;
    } else {
      return -1;
    }
  }
  void put(int key, int value) {
    MyListNode *cur=nullptr;
    if(lru_map.find(key)==lru_map.end()){
      if(lru_map.size()==cap){
        MyListNode *delete_node=tail;
        erase_list_node(delete_node);
        lru_map.erase(delete_node->key);
        delete delete_node;
      }
      cur=new MyListNode(key,value,nullptr,nullptr);
    } else {
      cur=lru_map[key];
      cur->value=value;
      erase_list_node(cur);
    }
    insert_front_list(cur);
    lru_map[key]=cur;
    return;
  }
private:
  void erase_list_node(MyListNode *cur) {
    if(cur==nullptr) return;
    MyListNode *prev=cur->pre,*nextv=cur->next;
    prev->next=nextv;
    if(nextv!=nullptr){
      nextv->pre=prev;
    }
    if(cur==tail){
      tail=prev;
    }
  }
  void insert_front_list(MyListNode *cur) {
    if(cur==nullptr) return;
    MyListNode *nextv=head->next;
    cur->next=head->next;
    if(nextv!=nullptr){
      nextv->pre=cur;
    }
    head->next=cur;
    cur->pre=head;
    if(tail==head){
      tail=cur;
    }
  }
private:
  int cap;
  MyListNode *head;
  MyListNode *tail;
  unordered_map<int, MyListNode*> lru_map;
};
//进阶二:手动实现list和map
template <typename K, typename V>
class HashMap {
public:
  HashMap(int size=10001) : table(size),value_count(0) {}
  virtual ~HashMap() {}
  bool insert(const K &key,const V &value) {
    const int index=my_hash(key)%table.size();
    typename list<pair<K,V>>::iterator iter=table[index].begin();
    for(;iter!=table[index].end();iter++) {
      if(iter->first==key){
        iter->second = value;
        return true;
      }
    }
    table[index].push_back(pair<K,V>(key,value));
    value_count++;
    return true;
  }
  bool search(const K &key, V &value) {
    const int index=my_hash(key)%table.size();
    typename list<pair<K,V>>::iterator iter=table[index].begin();
    for(;iter!=table[index].end();iter++) {
      if(iter->first==key){
        value=iter->second;
        return true;
      }
    }
    return false;
  }
  bool erase(const K &key) {
    const int index=my_hash(key)%table.size();
    typename list<pair<K,V>>::iterator iter=table[index].begin();
    for(;iter!=table[index].end();iter++) {
      if(iter->first==key){
        table[index].erase(iter);
        value_count--;
        return true;
      }
    }
    return false;
  }
  int get_value_count() { return value_count; }
private:
  int my_hash(const K &key) {
    return hash<K>()(key);
  }
private:
  vector<list<pair<K,V>>> table;
  int value_count;
};
struct MyListNode {
  int key;
  int value;
  MyListNode *next;
  MyListNode *pre;
  MyListNode():key(0),value(0),next(nullptr),pre(nullptr) {}
  MyListNode(int key_,int value_,MyListNode *next_,MyListNode *pre_):key(key_),value(value_),next(next_),pre(pre_) {}
};
class LRUCache {
public:
  LRUCache(int capacity) : cap(capacity)
  {
    head = new MyListNode();
    tail = head;
  }
  virtual ~LRUCache() {}
  int get(int key) {
    MyListNode *cur=nullptr;
    if(lru_map.search(key,cur)){
      int value=cur->value;
      erase_list_node(cur);
      insert_front_list(cur);
      return value;
    } else {
      return -1;
    }
  }
  void put(int key, int value) {
    MyListNode *cur=nullptr;
    if(!lru_map.search(key,cur)){
      if(lru_map.get_value_count()==cap){
        MyListNode *delete_node=tail;
        erase_list_node(delete_node);
        lru_map.erase(delete_node->key);
        delete delete_node;
      }
      cur=new MyListNode(key,value,nullptr,nullptr);
    } else {
      cur->value=value;
      erase_list_node(cur);
    }
    insert_front_list(cur);
    lru_map.insert(key,cur);
    return;
  }
private:
  void erase_list_node(MyListNode *cur) {
    if(cur==nullptr) return;
    MyListNode *prev=cur->pre,*nextv=cur->next;
    prev->next=nextv;
    if(nextv!=nullptr){
      nextv->pre=prev;
    }
    if(cur==tail){
      tail=prev;
    }
  }
  void insert_front_list(MyListNode *cur) {
    if(cur==nullptr) return;
    MyListNode *nextv=head->next;
    cur->next=head->next;
    if(nextv!=nullptr){
      nextv->pre=cur;
    }
    head->next=cur;
    cur->pre=head;
    if(tail==head){
      tail=cur;
    }
  }
private:
  int cap;
  MyListNode *head;
  MyListNode *tail;
  HashMap<int, MyListNode*> lru_map;
};
class LRUCache {
public:
	//基本思想:HashMap,时间复杂度O(N)
	LRUCache(int capacity) : capacity(capacity) {}
	int get(int key) {
		if (key_value.find(key) != key_value.end())
		{
			key_clock[key] = ++clock;
			return key_value[key];
		}
		else
			return -1;
	}
	void put(int key, int value) {
		if (key_value.find(key) != key_value.end())
		{
			key_value[key] = value;
			key_clock[key] = ++clock;
			return;
		}
		if (key_value.size() < capacity)
		{
			key_value[key] = value;
			key_clock[key] = ++clock;
		}
		else
		{
			int res_key, min_clock = clock;
			for (auto it = key_clock.begin(); it != key_clock.end(); it++)
			{
				if (it->second <= min_clock)
				{
					min_clock = it->second;
					res_key = it->first;
				}
			}
			key_value.erase(res_key);
			key_clock.erase(res_key);
			key_value[key] = value;
			key_clock[key] = ++clock;
		}
	}
private:
	int capacity;
	unordered_map<int, int> key_value;
	unordered_map<int, int> key_clock;
	int clock = 0;
};
class LRUCache1 {
public:
	//基本思想:HashMap+list,时间复杂度O(1),好的数据结构是成功的一半
	//list保存key->value对,从头到尾按照最近使用(头)到最少使用(尾)顺序排列
	//HashMap保存key->iterator对,iterator指向list中保存相应key的对,通过HashMap[key]可以快速找到key在list中位置
	LRUCache1(int capacity) : capacity(capacity) {}
	int get(int key) {
		if (key_value.find(key) != key_value.end())
		{
			int res = key_value[key]->second;
			listNode.erase(key_value[key]);
			listNode.push_front(make_pair(key, res));
			key_value[key] = listNode.begin();
			return res;
		}
		else
			return -1;
	}
	void put(int key, int value) {
		if (key_value.find(key) != key_value.end())
		{
			listNode.erase(key_value[key]);
			listNode.push_front(make_pair(key, value));
			key_value[key] = listNode.begin();
			return;
		}
		if (key_value.size() < capacity)
		{
			listNode.push_front(make_pair(key, value));
			key_value[key] = listNode.begin();
		}
		else
		{	
			key_value.erase(listNode.back().first);
			listNode.pop_back();
			listNode.push_front(make_pair(key, value));
			key_value[key] = listNode.begin();
		}
	}
private:
	int capacity;
	unordered_map<int, list<pair<int, int>>::iterator> key_value;
	list<pair<int, int>> listNode;    //链表的插入删除迭代器不会失效
};
int main()
{
	LRUCache1 cache(2);
	cout << cache.get(2) << endl;
	cache.put(2, 6);
	cout << cache.get(1) << endl;
	cache.put(1, 5);
	cache.put(1, 2);
	cout << cache.get(1) << endl;
	cout << cache.get(2) << endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值