Leetcode:LRUCache四个版本实现
题目
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
链接:https://oj.leetcode.com/problems/lru-cache/
分析
1:维护最近最少(LRU)使用的cache
1)使用count计数,每次操作cache时(get、set),该cache的count置0,其余cache的count加1,count最大的为最近最少使用的cache
2)使用链表,每次操作cache时(get、set),将该cache移动至链表头,链表尾的cache为最近最少使用的cache
2:快速查找cache
1)使用stl::unordered_map存储cache地址(内部hashTable)
版本一
使用std::list维护LRU,链表中存储cache实际空间。
Runtime: 248ms。
#include <list>
#include <unordered_map> struct KeyValue {
KeyValue(int pKey, int pValue):key(pKey), value(pValue) {};
int key;
int value;
}; class LRUCache{
public:
LRUCache(int capacity):_capacity(capacity) {}; int get(int key);
void set(int key, int value); private:
std::unordered_map<int, std::list<KeyValue>::iterator> keyToNodeItr;
std::list<KeyValue> lru; int _capacity;
}; void LRUCache::set(int key, int value) {
auto itr = keyToNodeItr.find(key);
if (itr != keyToNodeItr.end()) { // set value
itr->second->value = value;
KeyValue tmp(itr->second->key, itr->second->value);
keyToNodeItr.erase(itr->second->key);
lru.erase(itr->second);
lru.push_front(tmp);
} else { // insert value
if (lru.size() != _capacity) {
lru.push_front(KeyValue(key, value));
} else {
// pop back lru
if (lru.size() != ) {
keyToNodeItr.erase((lru.rbegin())->key);
lru.pop_back();
}
lru.push_front(KeyValue(key, value));
}
} keyToNodeItr.insert(std::pair<int, std::list<KeyValue>::iterator>(key, lru.begin()));
} int LRUCache::get(int key) {
auto itr = keyToNodeItr.find(key);
if (itr != keyToNodeItr.end()) {
int value = itr->second->value; KeyValue tmp(itr->second->key, itr->second->value);
keyToNodeItr.erase(itr->second->key);
lru.erase(itr->second);
lru.push_front(tmp);
keyToNodeItr.insert(std::pair<int, std::list<KeyValue>::iterator>(key, lru.begin())); return value;
}
return -;
}
因为链表中存储的为cache的实际空间,因此当需要改变cache的位置时,链表及map都需要改变,开销较大。
版本二
使用std::list维护LRU,链表中存储cache的指针。
Runtime:186ms。
#include <list>
#include <unordered_map> struct KeyValue {
KeyValue(int pKey, int pValue):key(pKey), value(pValue) {};
int key;
int value;
}; class LRUCache{
public:
LRUCache(int capacity):_capacity(capacity) {}; int get(int key);
void set(int key, int value); ~LRUCache(); private:
std::unordered_map<int, std::list<KeyValue*>::iterator> keyToNodeItr;
std::list<KeyValue*> lru; int _capacity;
}; LRUCache::~LRUCache() {
for (auto itr = lru.begin(); itr != lru.end(); ++itr) {
delete *itr;
}
} void LRUCache::set(int key, int value) {
auto itr = keyToNodeItr.find(key);
if (itr != keyToNodeItr.end()) { // set value
KeyValue* tmp = *(itr->second);
tmp->value = value;
lru.erase(itr->second);
lru.push_front(tmp);
itr->second = lru.begin(); // avoid invalid iterator
} else { // insert value
if (lru.size() == _capacity) { // pop back lru
KeyValue* tmp = *(lru.rbegin());
keyToNodeItr.erase(tmp->key);
delete tmp;
lru.pop_back();
}
lru.push_front(new KeyValue(key, value));
keyToNodeItr.insert(std::pair<int, std::list<KeyValue*>::iterator>(key, lru.begin()));
}
} int LRUCache::get(int key) {
auto itr = keyToNodeItr.find(key);
if (itr != keyToNodeItr.end()) {
KeyValue* kvPtr = *(itr->second);
lru.erase(itr->second);
lru.push_front(kvPtr);
itr->second = lru.begin();
return kvPtr->value;
}
return -;
}
需要注意的问题是map中存储的为list的迭代器,因此map中仍需要重新设置key到迭代器的映射,避免迭代器失效。
版本三
似乎std::list太笨重了,so实现轻量级双链表代替std::list。
Runtime: 77ms。
struct BiListNode {
BiListNode() {};
BiListNode(int key, int value):key(key), value(value) {};
int key;
int value;
BiListNode* pre;
BiListNode* next;
};
class BiList {
public:
BiList():_count() {
_head = new BiListNode();
_head->pre = _head;
_head->next = _head;
}
void push_front(BiListNode* pNode);
void move_front(BiListNode* pNode);
BiListNode* begin() {
return _head->next;
}
BiListNode* rbegin() {
return _head->pre;
}
void pop_back();
int size() { return _count; }
~BiList();
private:
BiListNode* _head;
int _count;
};
void BiList::push_front(BiListNode* pNode) {
pNode->next = _head->next;
pNode->pre = _head;
_head->next->pre = pNode;
_head->next = pNode;
if (_head->pre == _head) {
_head->pre = pNode;
}
++_count;
}
void BiList::move_front(BiListNode* pNode) {
if (pNode == _head->next) {
return;
}
pNode->pre->next = pNode->next;
pNode->next->pre = pNode->pre;
pNode->next = _head->next;
pNode->pre = _head;
_head->next->pre = pNode;
_head->next = pNode;
}
void BiList::pop_back() {
BiListNode* tailPtr = _head->pre;
tailPtr->pre->next = _head;
_head->pre = tailPtr->pre;
delete tailPtr;
--_count;
}
BiList::~BiList() {
for (BiListNode* itr = _head->next; itr != _head; itr = itr->next) {
delete itr;
}
delete _head;
}
class LRUCache {
public:
LRUCache(int capacity):_capacity(capacity) {};
int get(int key);
void set(int key, int value);
private:
int _capacity;
BiList biList;
std::unordered_map<int, BiListNode*> keyToNodePtr;
};
int LRUCache::get(int key) {
auto itr = keyToNodePtr.find(key);
if (itr != keyToNodePtr.end()) {
biList.move_front(itr->second);
return itr->second->value;
}
return -;
}
void LRUCache::set(int key, int value) {
auto itr = keyToNodePtr.find(key);
if (itr != keyToNodePtr.end()) { // set value
itr->second->value = value;
biList.move_front(itr->second);
} else { // insert
if (biList.size() == _capacity) {
keyToNodePtr.erase((biList.rbegin())->key);
biList.pop_back();
}
biList.push_front(new BiListNode(key, value));
keyToNodePtr.insert(std::pair<int, BiListNode*>(key, biList.begin()));
}
}
自己实现的双链表仅有80行代码,代码运行效率大大提高。
版本四
双链表都自己实现了,就死磕到底,再自己实现个开链哈希表吧。
Runtime:66ms。
struct BiListNode {
BiListNode() {};
BiListNode(int key, int value):key(key), value(value) {};
int key;
int value;
BiListNode* pre;
BiListNode* next;
};
class BiList {
public:
BiList():_count() {
_head = new BiListNode();
_head->pre = _head;
_head->next = _head;
}
void push_front(BiListNode* pNode);
void move_front(BiListNode* pNode);
BiListNode* begin() {
return _head->next;
}
BiListNode* rbegin() {
return _head->pre;
}
void pop_back();
int size() { return _count; }
~BiList();
private:
BiListNode* _head;
int _count;
};
void BiList::push_front(BiListNode* pNode) {
pNode->next = _head->next;
pNode->pre = _head;
_head->next->pre = pNode;
_head->next = pNode;
if (_head->pre == _head) {
_head->pre = pNode;
}
++_count;
}
void BiList::move_front(BiListNode* pNode) {
if (pNode == _head->next) {
return;
}
pNode->pre->next = pNode->next;
pNode->next->pre = pNode->pre;
pNode->next = _head->next;
pNode->pre = _head;
_head->next->pre = pNode;
_head->next = pNode;
}
void BiList::pop_back() {
BiListNode* tailPtr = _head->pre;
tailPtr->pre->next = _head;
_head->pre = tailPtr->pre;
delete tailPtr;
--_count;
}
BiList::~BiList() {
for (BiListNode* itr = _head->next; itr != _head; itr = itr->next) {
delete itr;
}
delete _head;
}
struct hashNode {
hashNode(int key, BiListNode* ptr):key(key), ptr(ptr), next(NULL) {};
int key;
BiListNode* ptr;
hashNode* next;
};
class HashTable {
public:
HashTable(int capacity);
hashNode* find(int key);
void insert(int key, BiListNode* ptr);
void erase(int key);
~HashTable();
private:
int _capacity;
hashNode** hashArray;
};
HashTable::HashTable(int capacity):_capacity(capacity) {
hashArray = new hashNode*[capacity];
for (int i = ; i < _capacity; ++i) {
hashArray[i] = NULL;
}
}
hashNode* HashTable::find(int key) {
for (hashNode* itr = hashArray[key % _capacity]; itr != NULL;
itr = itr->next) {
if (itr->key == key) {
return itr;
}
}
return NULL;
}
void HashTable::insert(int key, BiListNode* ptr) {
hashNode* tmp = new hashNode(key, ptr);
int relativeKey = key % _capacity;
if (hashArray[relativeKey] == NULL) {
hashArray[relativeKey] = tmp;
return;
}
tmp->next = hashArray[relativeKey];
hashArray[relativeKey] = tmp;
}
void HashTable::erase(int key) {
for (hashNode* pre = hashArray[key % _capacity], *itr = pre;
itr != NULL; pre = itr, itr = itr->next) {
if (itr->key == key) {
if (itr != pre)
pre->next = itr->next;
else // head
hashArray[key % _capacity] = itr->next;
delete itr;
}
}
}
HashTable::~HashTable() {
for (int i = ; i < _capacity; ++i) {
for (hashNode* itr = hashArray[i]; itr != NULL;) {
hashNode* tmp = itr;
itr = itr->next;
delete tmp;
}
}
delete [] hashArray;
}
class LRUCache {
public:
LRUCache(int capacity):_capacity(capacity) {
hashTable = new HashTable();
};
int get(int key);
void set(int key, int value);
~LRUCache() { delete hashTable; }
private:
int _capacity;
BiList bilist;
HashTable* hashTable;
};
int LRUCache::get(int key) {
hashNode* tmp = hashTable->find(key);
if (tmp != NULL) {
bilist.move_front(tmp->ptr);
return tmp->ptr->value;
}
return -;
}
void LRUCache::set(int key, int value) {
hashNode* tmp = hashTable->find(key);
if (tmp != NULL) { // set
bilist.move_front(tmp->ptr);
tmp->ptr->value = value;
return;
}
// insert
if (bilist.size() == _capacity) {
hashTable->erase((bilist.rbegin())->key);
bilist.pop_back();
}
bilist.push_front(new BiListNode(key, value));
hashTable->insert(key, bilist.begin());
}
开链哈希表72行代码
Leetcode:LRUCache四个版本实现的更多相关文章
- 《玩转D语言系列》一、通过四个版本的 Hello Word 初识D语言
对于D语言,相信很多朋友还没听说过,因为它还不够流行,跟出自名门的一些语言比起来也没有名气,不过这并不影响我对它的偏爱,我就是这样的一种人,我喜欢的女孩子一定是知己型,而不会因为她外表,出身,学历,工 ...
- LeetCode第四天
leetcode 第四天 2018年1月4日 15.(628)Maximum Product of Three Numbers JAVA class Solution { public int max ...
- Curved UI - VR Ready Solution To Bend Warp Your Canvas 1.7,1.8,2.2,2.3 四种版本压缩包(Unity UGUI曲面插件),可以兼容VRTK
Curved UI - VR Ready Solution To Bend Warp Your Canvas 1.7,1.8,2.2,2.3 四种版本压缩包(Unity UGUI曲面插件) 可以兼容V ...
- LeetCode:四数之和【18】
LeetCode:四数之和[18] 题目描述 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c ...
- [LeetCode] Compare Version Numbers 版本比较
Compare two version numbers version1 and version1.If version1 > version2 return 1, if version1 &l ...
- [LeetCode] 4Sum 四数之和
Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = tar ...
- Django Rest Framework源码剖析(四)-----API版本
一.简介 在我们给外部提供的API中,可会存在多个版本,不同的版本可能对应的功能不同,所以这时候版本使用就显得尤为重要,django rest framework也为我们提供了多种版本使用方法. 二. ...
- 【LeetCode】四数之和【排序,固定k1,k2,二分寻找k3和k4】
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满 ...
- Java实现 LeetCode 18 四数之和
18. 四数之和 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target ...
随机推荐
- HDOJ 1202 The calculation of GPA
Problem Description 每学期的期末,大家都会忙于计算自己的平均成绩,这个成绩对于评奖学金是直接有关的.国外大学都是计算GPA(grade point average) 又称GPR(g ...
- Makefile如何通过宏开关进行条件编译
在开发中经常会遇到需要条件编译一段代码,即: #ifdef DEBUG { 如果定义了DUBUG,则执行此段代码!} #else {否则执行此段代码!} 这就需要通过宏开关来进行条件编译,也就是常说的 ...
- libvirtVirsh
virsh基于不同协议远程连接libvirt drivers(Hypervisors) 基于OS系统账号ssh登陆
- WPF自定义下拉控件
可以搜索的下拉条 using System; using System.Collections; using System.Collections.Generic; using System.Coll ...
- 谈谈Ext JS的组件——布局的用法
概述 在Ext JS中.包括两类布局:组件类布局和容器类布局.由于有些组件是有不同的组件组合而成的,如字段就由标题和输入框构成,他们之间也是存在布局关系的,而这就须要组件类布局来处理组件内自己特有的布 ...
- [ES6] Module export
Default export: Default export is easy way to export a function to outside module. //flash-message.j ...
- 如何使用easyUI
一.简介 以下内容来自百度: jQuery EasyUI是一组基于jQuery的UI插件集合,而jQuery EasyUI的 目标就是帮助web开发者更轻松的打造出功能丰富并且美观的UI界面.开发者不 ...
- Qt学习博客推荐
附录C Qt资源 C.1 Qt 官方资源 全 球各大公司以及独立开发人员每天都在加入 Qt 的开发社区.他们已经认识到了Qt 的架构本身便可加快应用程序开发进度.这些开发人员,无论是想开发单平台软件. ...
- css布局&初始化&基准样式
学习css布局比较好的网站 学习css布局 1.css设置模块 typography(字体) colour(颜色) link(链接) forms(表单) layout(布局) navigation(导 ...
- js生成随机数的方法小结
js生成随机数主要用到了内置的Math对象的random()方法.用法如:Math.random().它返回的是一个 0 ~ 1 之间的随机数.有了这么一个方法,那生成任意随机数就好理解了.比如实际中 ...