运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
进阶:
你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例:
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
struct Double_list;
struct Hash_table{
int key;
int value;
struct Double_list *list_pos;
//同一hash用链表解决冲突
struct Hash_table *next_entry;
};
struct Double_list{
struct Hash_table *hash;
struct Double_list *previous;
struct Double_list *next;
};
typedef struct {
int table_size;
int total_entry;
struct Hash_table **hash_table;//桶大小由table_size决定
struct Double_list *head;
struct Double_list *end;
} LRUCache;
static inline int get_hash(int key)
{
return (key<<16)+(key<<8)+key;
}
void show(struct Double_list * copy)
{
printf("\n|-");
while(copy)
{
printf("%d-",copy->hash->key);
copy=copy->next;
}
printf("|\n");
}
LRUCache* lRUCacheCreate(int capacity) {
assert(capacity>0);
LRUCache* ret=(LRUCache*)malloc(sizeof(LRUCache));
ret->table_size=capacity;
ret->hash_table=(struct Hash_table * *)calloc(ret->table_size,sizeof(struct Hash_table *));
ret->total_entry=0;
ret->head=NULL;
ret->end=NULL;
return ret;
}
int lRUCacheGet(LRUCache* obj, int key) {
int ret=-1;
struct Hash_table * hash=obj->hash_table[get_hash(key)%obj->table_size];
while(hash)
{
if(hash->key==key)
{
ret=hash->value;
if(hash->list_pos->previous&&hash->list_pos->next)
{
hash->list_pos->previous->next=hash->list_pos->next;
hash->list_pos->next->previous=hash->list_pos->previous;
hash->list_pos->previous=NULL;
hash->list_pos->next=obj->head;
obj->head->previous=hash->list_pos;
obj->head=hash->list_pos;
}
else if(!hash->list_pos->previous)
{//为头节点,无需移动
}
else
{//为尾部节点
obj->end=obj->end->previous;
obj->end->next=NULL;
hash->list_pos->next=obj->head;
hash->list_pos->previous=NULL;
obj->head->previous=hash->list_pos;
obj->head=hash->list_pos;
}
}
hash=hash->next_entry;
}
//show(obj->head);
return ret;
}
void lRUCachePut(LRUCache* obj, int key, int value) {
int hash_key=get_hash(key)%obj->table_size;
if(lRUCacheGet(obj,key)!=(-1))
{
struct Hash_table *tmp=obj->hash_table[hash_key];
while(tmp)
{
if(tmp->key==key)
{
tmp->value=value;
break;
}
tmp=tmp->next_entry;
}
return;
}
if(obj->total_entry>=obj->table_size)
{//桶满,需删除元素
int erase_key=obj->end->hash->key;
struct Hash_table * hash=obj->hash_table[get_hash(erase_key)%obj->table_size];
struct Hash_table * tmp=NULL;
while(hash)
{//从hash表中移除
if(hash->key==erase_key)
{
tmp=hash;
obj->hash_table[get_hash(erase_key)%obj->table_size]=hash->next_entry;
break;
}
if(hash->next_entry->key==erase_key)
{
tmp=hash->next_entry;
hash->next_entry=hash->next_entry->next_entry;
break;
}
hash=hash->next_entry;
}
//从访问双向链表中移除
if(tmp->list_pos->previous)
{
obj->end=tmp->list_pos->previous;
obj->end->next=NULL;
free(tmp->list_pos);
free(tmp);
}
else
{
obj->end=NULL;
obj->head=NULL;
}
obj->total_entry--;
}
obj->total_entry++;
struct Hash_table * new_entry=(struct Hash_table *)malloc(sizeof(struct Hash_table));
new_entry->key=key;
new_entry->value=value;
new_entry->next_entry=NULL;
new_entry->list_pos=(struct Double_list*)malloc(sizeof (struct Double_list));
new_entry->list_pos->hash=new_entry;
//加入访问链表头部
new_entry->list_pos->previous=NULL;
new_entry->list_pos->next=obj->head;
if(obj->head)obj->head->previous=new_entry->list_pos;
if(!obj->end)obj->end=new_entry->list_pos;
obj->head=new_entry->list_pos;
//插入至hash表
struct Hash_table * hash=obj->hash_table[hash_key];
if(!hash)obj->hash_table[hash_key]=new_entry;
else
{
while(hash->next_entry!=NULL)hash=hash->next_entry;
hash->next_entry=new_entry;
}
//show(obj->head);
}
void lRUCacheFree(LRUCache* obj) {
free(obj->hash_table);
struct Hash_table * hash=NULL;
struct Double_list * tmp=NULL;
while(obj->head)
{
tmp=obj->head;
obj->head=obj->head->next;
hash=tmp->hash;
free(tmp);
free(hash);
}
free(obj);
}
/**
* Your LRUCache struct will be instantiated and called as such:
* LRUCache* obj = lRUCacheCreate(capacity);
* int param_1 = lRUCacheGet(obj, key);
* lRUCachePut(obj, key, value);
* lRUCacheFree(obj);
*/
执行用时 :116 ms, 在所有 C 提交中击败了90.74% 的用户
内存消耗 :26.6 MB, 在所有 C 提交中击败了50.00%的用户