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 }

因为链表中存储的为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 }

需要注意的问题是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 }

自己实现的双链表仅有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 }

开链哈希表72行代码

时间: 2024-08-02 07:59:44

Leetcode:LRUCache四个版本实现的相关文章

【一】通过四个版本的 Hello Word 初识D语言

对于D语言,相信很多朋友还没听说过,因为它还不够流行,跟出自名门的一些语言比起来也没有名气,不过这并不影响我对它的偏爱,我就是这样的一种人,我喜欢的女孩子一定是知己型,而不会因为她外表,出身,学历,工作等产生偏见,如果您和我一样相信您也会喜欢上D语言,作为一个工作上目前几乎用不到的语言还是很值得学习,就像是跟你结婚的人未必是你最喜欢的人(做人留一线,见后好 xx 嘛,\(^o^)/ ) 好了,进入正题,本文将使用四个版本的Hello Word程序向还不了解D语言,有兴趣学习新语言的朋友们展示D语

LeetCode第四题,Add Two Numbers

题目原文: You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. Input: (2 -> 4 -> 3) + (5 -> 6

LeetCode第四天

leetcode 第四天 2018年1月4日 15.(628)Maximum Product of Three Numbers JAVA class Solution { public int maximumProduct(int[] nums) { Arrays.sort(nums); return (nums[0]*nums[1]*nums[nums.length-1])>(nums[nums.length-1]*nums[nums.length-2]*nums[nums.length-3]

LeetCode:四数之和【18】

LeetCode:四数之和[18] 题目描述 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组. 注意: 答案中不可以包含重复的四元组. 示例: 给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0. 满足要求的四元组集合为:[ [-1, 0, 0, 1], [-2, -1, 1, 2]

UNIX网络编程卷1 回射服务器程序 TCP服务器程序设计范式 四个版本

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 这是一个简单的回射服务器程序.它将客户发送的数据读入缓冲区并回射其中内容 下面我会介绍同一个使用 TCP 协议的回射服务器程序的几个不同版本,分别是 fork 版本.select 版本.poll 版本.多线程版本 fork 版本:为每一个客户连接派生(fork) 一个子进程用来处理客户请求 /** * TCP/IPv4 协议相关 * **/ #include "unp.h" in

[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 = target? Find all unique quadruplets in the array which gives the sum of target. Note: Elements in a quadruplet (a,b,c,d) must be in non-descending order.

git学习(四)----版本跳转

git log .git reflog .git reset Git版本跳转: 想实现版本跳转,需要先了解Git的版本的标记,也就是通过这个唯一的标记能定位一个版本,也就是commit id. 这个commit id是个挺长的字符串,因为它是通过SHA-1计算出来.(这个字符串就跟你硬盘里的某些磁力链接挺像的,嘿嘿嘿!),因为git是分布式的,所以每个人电脑上都有完整代码,如果你更新了别人文件到本机上了,他提交的commit id是简单的自增数字(1,2,3....),就有可能会和你本机上的co

[LeetCode] Compare Version Numbers 版本比较

Compare two version numbers version1 and version1.If version1 > version2 return 1, if version1 < version2 return -1, otherwise return 0. You may assume that the version strings are non-empty and contain only digits and the . character.The . characte

LeetCode 18. 四数之和(4Sum)

题目描述 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组. 注意: 答案中不可以包含重复的四元组. 示例: 给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0. 满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]