24. LFU缓存
描述
LFU是一个著名的缓存算法
对于容量为k的缓存,如果缓存已满,并且需要逐出其中的密钥,则最少使用的密钥将被踢出。
实现LFU中的set
和 get
样例
Input: LFUCache(3) set(2,2) set(1,1) get(2) get(1) get(2) set(3,3) set(4,4) get(3) get(2) get(1) get(4)
Output:
2
1
2
-1
2
1
4
题解:
class LFUCache {
private:
struct ListNode {
int key;
int value;
ListNode* prev;
ListNode* next;
ListNode(int k, int v) : key(k), value(v), prev(NULL), next(NULL) {};
};
struct BucketNode {
int freq;
ListNode* head;
ListNode* tail;
BucketNode* prev;
BucketNode* next;
BucketNode(int f) : freq(f), prev(NULL), next(NULL), head(NULL), tail(NULL) {};
void removeOne(ListNode* node) {
if (node == head && node == tail) {
tail = head = NULL;
} else if (node == head) {
node->next->prev = NULL;
head = node->next;
} else if (node == tail) {
node->prev->next = NULL;
tail = node->prev;
} else {
node->prev->next = node->next;
node->next->prev = node->prev;
}
node->prev = node->next = NULL;
}
void addOne(ListNode* node) {
if (head == NULL) {
head = tail = node;
} else {
head->prev = node;
node->next = head;
head = node;
}
}
bool empty() { return head == NULL; }
};
public:
LFUCache(int capacity) : mCapacity(capacity) {
head = tail = NULL;
}
int get(int key) {
if (mCapacity == 0) return -1;
ListNode* node = promote(key);
return node ? node->value : -1;
}
void set(int key, int value) {
if (mCapacity == 0) return;
ListNode* node = promote(key);
if (node) {
node->value = value;
return;
} else {
if (freqTable.size() >= mCapacity) evictLast();
node = new ListNode(key, value);
BucketNode* bucket;
if (tail == NULL || tail->freq != 1) {
bucket = new BucketNode(1);
bucket->addOne(node);
if (tail == NULL) {
head = tail = bucket;
} else {
bucket->prev = tail;
tail->next = bucket;
tail = bucket;
}
} else {
bucket = tail;
bucket->addOne(node);
}
freqTable[key] = pair<BucketNode*, ListNode*>(bucket, node);
}
}
private:
inline ListNode* promote(int key) {
auto it = freqTable.find(key);
if (it == freqTable.end()) return NULL;
BucketNode* bucket = it->second.first;
ListNode* node = it->second.second;
bucket->removeOne(node);
BucketNode* newBucket;
if (bucket->prev == NULL || bucket->prev->freq != bucket->freq + 1) {
newBucket = new BucketNode(bucket->freq + 1);
newBucket->addOne(node);
if (bucket->prev == NULL) {
head = newBucket;
newBucket->next = bucket;
bucket->prev = newBucket;
} else {
bucket->prev->next = newBucket;
newBucket->prev = bucket->prev;
newBucket->next = bucket;
bucket->prev = newBucket;
}
} else {
newBucket = bucket->prev;
newBucket->addOne(node);
}
it->second.first = newBucket;
it->second.second = node;
if (bucket->empty()) removeBucket(bucket);
return node;
}
inline void removeBucket(BucketNode* bucket) {
if (bucket == head && bucket == tail) {
head = tail = NULL;
} else if (bucket == head) {
bucket->next->prev = NULL;
head = bucket->next;
} else if (bucket == tail) {
bucket->prev->next = NULL;
tail = bucket->prev;
} else {
bucket->prev->next = bucket->next;
bucket->next->prev = bucket->prev;
}
delete bucket;
}
inline void evictLast() {
ListNode* node = tail->tail;
freqTable.erase(node->key);
tail->removeOne(node);
delete node;
if (tail->empty()) removeBucket(tail);
}
private:
int mCapacity;
BucketNode* head;
BucketNode* tail;
unordered_map<int, pair<BucketNode*, ListNode*>> freqTable;
};