LeetCode OJ上有一道题目,要编程实现cache的LRU算法,刚看到这道题的时候,我想到了用队列来做,但是若用单链表来做,必须要保存尾节点的上一个节点指针,才能实现快速增加一条数据,编程起来很不方便,所以我采用了双端队列实现,为了处理方便,保存两个带节点指针,一个节点的next指向头结点,另一个节点next指向尾节点。
LRU算法中还要求将已存在的节点放入队尾或者插入节点时要判断此节点是不是存在,所以我使用hash表来快速的定位到此节点,所以就有节点定义
struct node{
int key;
int val;
node *pre;
node *next;
node(int k,int v):key(k),val(v),pre(NULL),next(NULL){}
};
hash定义:
#define MAX 10000//hash表最大长度
#define MOD 9991//大质数
node *Hash[MAX][20];
int len[MAX];//对应每个键值的长度
int _hash(int key){
return key%MOD;
}
其中插入节点时需要判断该节点是不是存在,若存在,需要将其放到队尾,并给其赋新值;根据key值获取节点value值时也要判断节点是否存在,若存在也需将其放入队尾,并返回该节点value值。所以我增加了一个search函数,
函数声明为:
node * search(int key);
该函数功能为,在hash表中查找键值为key的节点,若找不到返回NULL,找到时将该节点拿出来插入到队尾,并返回该节点,具体实现为:
node * search(int key){
int pos=_hash(key);//找到hash表位置
for(int i=0;ikey){
node *t=Hash[pos][i];
if(t->pre==NULL&&t->next==NULL)return t;//如果该队列中只有一个元素时返回该节点
if(t->pre==NULL){//若为头结点,还需要对head指针进行操作
head->next=t->next;
t->next->pre=NULL;
t->pre=tail->next;
tail->next->next=t;
tail->next=t;
return t;
}
if(t->next==NULL){//若为尾节点
return t;
}
t->pre->next=t->next;//既不是头结点也不是尾节点,只需要对tail指针进行操作
t->next->pre=t->pre;
t->pre=tail->next;
tail->next->next=t;
t->next=NULL;
tail->next=t;
return t;
}
}
return NULL;
}
所以整个DQueue双端队列类实现如下:
class DQueue{
node *head;
node *tail;
int Size;
node *Hash[MAX][20];
int len[MAX];
int _hash(int key){
return key%MOD;
}
node * search(int key){
int pos=_hash(key);
for(int i=0;ikey){
node *t=Hash[pos][i];
if(t->pre==NULL&&t->next==NULL)return t;
if(t->pre==NULL){
head->next=t->next;
t->next->pre=NULL;
t->pre=tail->next;
tail->next->next=t;
tail->next=t;
return t;
}
if(t->next==NULL){
return t;
}
t->pre->next=t->next;
t->next->pre=t->pre;
t->pre=tail->next;
tail->next->next=t;
t->next=NULL;
tail->next=t;
return t;
}
}
return NULL;
}
public:
DQueue(){
head=new node(0,0);
tail=new node(0,0);
memset(len,0,sizeof(len));
Size=0;
}
int insert(int key,int val){
node *i=search(key);
if(i!=NULL){
i->val=val;
return 1;
}
node *t=new node(key,val);
if(!Size){
head->next=t;
}
t->pre=tail->next;
if(tail->next!=NULL)tail->next->next=t;
int pos=_hash(t->key);
Hash[pos][len[pos]++]=t;
tail->next=t;
Size++;
return 1;
}
int delHead(){
node *p=head->next;
if(p!=NULL){
head->next=p->next;
if(p->next!=NULL)p->next->pre=NULL;
delete[] p;
Size--;
return 1;
}
return 0;
}
int find(int key){
node *f=search(key);
if(f!=NULL)return f->val;
return -1;
}
int size(){
return Size;
}
};
LRU类实现就比较简单了:
class LRUCache{
DQueue *dq;
int capa;
public:
LRUCache(int capacity){
capa=capacity;
dq=new DQueue();
}
int get(int key){
return dq->find(key);
}
void set(int key,int value){
dq->insert(key,value);//先插入一个节点
if(dq->size()>capa){//若队列满则删掉头结点
dq->delHead();
}
}
};
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct node{
int key;
int val;
node *pre;
node *next;
node(int k,int v):key(k),val(v),pre(this),next(this){}
};
#include
#include
#include
#include
#include
#include
#include
#include
#include