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。

 1 #include <list>

 2 #include <unordered_map>

 3 

 4 struct KeyValue {

 5     KeyValue(int pKey, int pValue):key(pKey), value(pValue) {};

 6     int key;

 7     int value;

 8 };

 9 

10 class LRUCache{

11 public:

12     LRUCache(int capacity):_capacity(capacity) {};

13     

14     int get(int key);

15     void set(int key, int value);

16     

17 private:

18     std::unordered_map<int, std::list<KeyValue>::iterator> keyToNodeItr;

19     std::list<KeyValue> lru;

20     

21     int _capacity;

22 };

23 

24 void LRUCache::set(int key, int value) {

25     auto itr = keyToNodeItr.find(key);

26     if (itr != keyToNodeItr.end()) { // set value

27         itr->second->value = value;

28         KeyValue tmp(itr->second->key, itr->second->value);

29         keyToNodeItr.erase(itr->second->key);

30         lru.erase(itr->second);

31         lru.push_front(tmp);

32     } else { // insert value

33         if (lru.size() != _capacity) {

34             lru.push_front(KeyValue(key, value));

35         } else {

36             // pop back lru

37             if (lru.size() != 0) {

38                 keyToNodeItr.erase((lru.rbegin())->key);

39                 lru.pop_back();

40             }

41             lru.push_front(KeyValue(key, value));

42         }

43     }

44     

45     keyToNodeItr.insert(std::pair<int, std::list<KeyValue>::iterator>(key, lru.begin()));

46 }

47 

48 int LRUCache::get(int key) {

49     auto itr = keyToNodeItr.find(key);

50     if (itr != keyToNodeItr.end()) {

51         int value = itr->second->value;

52         

53         KeyValue tmp(itr->second->key, itr->second->value);

54         keyToNodeItr.erase(itr->second->key);

55         lru.erase(itr->second);

56         lru.push_front(tmp);

57         keyToNodeItr.insert(std::pair<int, std::list<KeyValue>::iterator>(key, lru.begin()));

58         

59         return value;

60     }

61     return -1;

62 }
View Code

因为链表中存储的为cache的实际空间,因此当需要改变cache的位置时,链表及map都需要改变,开销较大。

 

版本二

使用std::list维护LRU,链表中存储cache的指针。

Runtime:186ms。

 1 #include <list>

 2 #include <unordered_map>

 3 

 4 struct KeyValue {

 5     KeyValue(int pKey, int pValue):key(pKey), value(pValue) {};

 6     int key;

 7     int value;

 8 };

 9 

10 class LRUCache{

11 public:

12     LRUCache(int capacity):_capacity(capacity) {};

13     

14     int get(int key);

15     void set(int key, int value);

16     

17     ~LRUCache();

18     

19 private:

20     std::unordered_map<int, std::list<KeyValue*>::iterator> keyToNodeItr;

21     std::list<KeyValue*> lru;

22     

23     int _capacity;

24 };

25 

26 LRUCache::~LRUCache() {

27     for (auto itr = lru.begin(); itr != lru.end(); ++itr) {

28         delete *itr;

29     }

30 }

31 

32 void LRUCache::set(int key, int value) {    

33     auto itr = keyToNodeItr.find(key);

34     if (itr != keyToNodeItr.end()) { // set value

35         KeyValue* tmp = *(itr->second);

36         tmp->value = value;

37         lru.erase(itr->second);

38         lru.push_front(tmp);

39         itr->second = lru.begin(); // avoid invalid iterator

40     } else { // insert value

41         if (lru.size() == _capacity) { // pop back lru

42             KeyValue* tmp = *(lru.rbegin());

43             keyToNodeItr.erase(tmp->key);

44             delete tmp;

45             lru.pop_back();

46         }

47         lru.push_front(new KeyValue(key, value));

48         keyToNodeItr.insert(std::pair<int, std::list<KeyValue*>::iterator>(key, lru.begin()));

49     }

50 }

51 

52 int LRUCache::get(int key) {

53     auto itr = keyToNodeItr.find(key);

54     if (itr != keyToNodeItr.end()) {

55         KeyValue* kvPtr = *(itr->second);

56         lru.erase(itr->second);

57         lru.push_front(kvPtr);

58         itr->second = lru.begin();

59         return kvPtr->value;

60     }

61     return -1;

62 }
View Code

需要注意的问题是map中存储的为list的迭代器,因此map中仍需要重新设置key到迭代器的映射,避免迭代器失效。

 

版本三

似乎std::list太笨重了,so实现轻量级双链表代替std::list。

Runtime: 77ms。

  1 struct BiListNode {

  2     BiListNode() {};

  3     BiListNode(int key, int value):key(key), value(value) {};

  4     int key;

  5     int value;

  6     BiListNode* pre;

  7     BiListNode* next;

  8 };

  9 

 10 class BiList {

 11 public:

 12     BiList():_count(0) {

 13         _head = new BiListNode();

 14         _head->pre = _head;

 15         _head->next = _head;

 16     }

 17 

 18     void push_front(BiListNode* pNode);

 19 

 20     void move_front(BiListNode* pNode);

 21     

 22     BiListNode* begin() {

 23         return _head->next;

 24     }

 25     

 26     BiListNode* rbegin() {

 27         return _head->pre;

 28     }

 29     

 30     void pop_back();

 31 

 32     int size() { return _count; }

 33 

 34     ~BiList();

 35 

 36 private:

 37     BiListNode* _head;

 38     int _count;

 39 };

 40 

 41 void BiList::push_front(BiListNode* pNode) {

 42     pNode->next = _head->next;

 43     pNode->pre = _head;

 44     _head->next->pre = pNode;

 45     _head->next = pNode;

 46     if (_head->pre == _head) {

 47         _head->pre = pNode;

 48     }

 49     ++_count;

 50 }

 51 

 52 void BiList::move_front(BiListNode* pNode) {

 53     if (pNode == _head->next) {

 54         return;

 55     }

 56     pNode->pre->next = pNode->next;

 57     pNode->next->pre = pNode->pre;

 58     

 59     pNode->next = _head->next;

 60     pNode->pre = _head;

 61     

 62     _head->next->pre = pNode;

 63     

 64     _head->next = pNode;

 65 }

 66 

 67 void BiList::pop_back() {

 68     BiListNode* tailPtr = _head->pre;

 69     tailPtr->pre->next = _head;

 70     _head->pre = tailPtr->pre;

 71     delete tailPtr;

 72     --_count;

 73 }

 74 

 75 BiList::~BiList() {

 76     for (BiListNode* itr = _head->next; itr != _head; itr = itr->next) {

 77         delete itr;

 78     }

 79     delete _head;

 80 }

 81 

 82 

 83 class LRUCache {

 84 public:

 85     LRUCache(int capacity):_capacity(capacity) {};

 86     

 87     int get(int key);

 88     

 89     void set(int key, int value);

 90     

 91 private:

 92     int _capacity;

 93     

 94     BiList biList;

 95     std::unordered_map<int, BiListNode*> keyToNodePtr;

 96 };

 97 

 98 int LRUCache::get(int key) {

 99     auto itr = keyToNodePtr.find(key);

100     if (itr != keyToNodePtr.end()) {

101         biList.move_front(itr->second);

102         return itr->second->value;

103     }

104     return -1;

105 }

106 

107 void LRUCache::set(int key, int value) {

108     auto itr = keyToNodePtr.find(key);

109     if (itr != keyToNodePtr.end()) { // set value

110         itr->second->value = value;

111         biList.move_front(itr->second);

112     } else { // insert

113         if (biList.size() == _capacity) {

114             keyToNodePtr.erase((biList.rbegin())->key);

115             biList.pop_back();

116         }

117         biList.push_front(new BiListNode(key, value));

118         keyToNodePtr.insert(std::pair<int, BiListNode*>(key, biList.begin()));

119     }

120 }
View Code

自己实现的双链表仅有80行代码,代码运行效率大大提高。

 

版本四

双链表都自己实现了,就死磕到底,再自己实现个开链哈希表吧。

Runtime:66ms。

  1 struct BiListNode {

  2     BiListNode() {};

  3     BiListNode(int key, int value):key(key), value(value) {};

  4     int key;

  5     int value;

  6     BiListNode* pre;

  7     BiListNode* next;

  8 };

  9 

 10 class BiList {

 11 public:

 12     BiList():_count(0) {

 13         _head = new BiListNode();

 14         _head->pre = _head;

 15         _head->next = _head;

 16     }

 17 

 18     void push_front(BiListNode* pNode);

 19 

 20     void move_front(BiListNode* pNode);

 21     

 22     BiListNode* begin() {

 23         return _head->next;

 24     }

 25     

 26     BiListNode* rbegin() {

 27         return _head->pre;

 28     }

 29     

 30     void pop_back();

 31 

 32     int size() { return _count; }

 33 

 34     ~BiList();

 35 

 36 private:

 37     BiListNode* _head;

 38     int _count;

 39 };

 40 

 41 void BiList::push_front(BiListNode* pNode) {

 42     pNode->next = _head->next;

 43     pNode->pre = _head;

 44     _head->next->pre = pNode;

 45     _head->next = pNode;

 46     if (_head->pre == _head) {

 47         _head->pre = pNode;

 48     }

 49     ++_count;

 50 }

 51 

 52 void BiList::move_front(BiListNode* pNode) {

 53     if (pNode == _head->next) {

 54         return;

 55     }

 56     pNode->pre->next = pNode->next;

 57     pNode->next->pre = pNode->pre;

 58     

 59     pNode->next = _head->next;

 60     pNode->pre = _head;

 61     

 62     _head->next->pre = pNode;

 63     

 64     _head->next = pNode;

 65 }

 66 

 67 void BiList::pop_back() {

 68     BiListNode* tailPtr = _head->pre;

 69     tailPtr->pre->next = _head;

 70     _head->pre = tailPtr->pre;

 71     delete tailPtr;

 72     --_count;

 73 }

 74 

 75 BiList::~BiList() {

 76     for (BiListNode* itr = _head->next; itr != _head; itr = itr->next) {

 77         delete itr;

 78     }

 79     delete _head;

 80 }

 81 

 82 struct hashNode {

 83     hashNode(int key, BiListNode* ptr):key(key), ptr(ptr), next(NULL) {};

 84     int key;

 85     BiListNode* ptr;

 86     hashNode* next;

 87 };

 88 

 89 class HashTable {

 90 public:

 91     HashTable(int capacity);

 92     

 93     hashNode* find(int key);

 94     

 95     void insert(int key, BiListNode* ptr);

 96     

 97     void erase(int key);

 98     

 99     ~HashTable();

100     

101 private:

102     int _capacity;

103     hashNode** hashArray;

104 };

105 

106 HashTable::HashTable(int capacity):_capacity(capacity) {

107     hashArray = new hashNode*[capacity];

108     for (int i = 0; i < _capacity; ++i) {

109         hashArray[i] = NULL;

110     }

111 }

112 

113 hashNode* HashTable::find(int key) {

114     for (hashNode* itr = hashArray[key % _capacity]; itr != NULL;

115             itr = itr->next) {

116         if (itr->key == key) {

117             return itr;

118         }

119     }

120     return NULL;

121 }

122 

123 void HashTable::insert(int key, BiListNode* ptr) {

124     hashNode* tmp = new hashNode(key, ptr);

125     

126     int relativeKey = key % _capacity;

127     

128     if (hashArray[relativeKey] == NULL) {

129         hashArray[relativeKey] = tmp;

130         return;

131     }

132 

133     tmp->next = hashArray[relativeKey];

134     hashArray[relativeKey] = tmp;

135 }

136 

137 void HashTable::erase(int key) {

138     for (hashNode* pre = hashArray[key % _capacity], *itr = pre;

139             itr != NULL; pre = itr, itr = itr->next) {

140         if (itr->key == key) {

141             if (itr != pre)

142                 pre->next = itr->next;

143             else // head

144                 hashArray[key % _capacity] = itr->next;

145 

146             delete itr;

147         }

148     }

149 }

150 

151 HashTable::~HashTable() {

152     for (int i = 0; i < _capacity; ++i) {

153         for (hashNode* itr = hashArray[i]; itr != NULL;) {

154             hashNode* tmp = itr;

155             itr = itr->next;

156             delete tmp;

157         }

158     }

159     delete [] hashArray;

160 }

161 

162 class LRUCache {

163 public:

164     LRUCache(int capacity):_capacity(capacity) {

165         hashTable = new HashTable(1024);

166     };

167     

168     int get(int key);

169     

170     void set(int key, int value);

171     

172     ~LRUCache() { delete hashTable; }

173     

174 private:

175     int _capacity;

176     BiList bilist;

177     HashTable* hashTable;

178 };

179 

180 int LRUCache::get(int key) {

181     hashNode* tmp = hashTable->find(key);

182     if (tmp != NULL) {

183         bilist.move_front(tmp->ptr);

184         return tmp->ptr->value;

185     }

186     return -1;

187 }

188 

189 void LRUCache::set(int key, int value) {

190     hashNode* tmp = hashTable->find(key);

191     if (tmp != NULL) { // set

192         bilist.move_front(tmp->ptr);

193         tmp->ptr->value = value;

194         return;

195     }

196     

197     // insert

198     if (bilist.size() == _capacity) {

199         hashTable->erase((bilist.rbegin())->key);

200         bilist.pop_back();

201     }

202 

203     bilist.push_front(new BiListNode(key, value));

204     hashTable->insert(key, bilist.begin());

205 }
View Code

开链哈希表72行代码

你可能感兴趣的:(LeetCode)