2014.2.13 18:15
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.
Solution:
My LRU cache is made up of two parts, a hash table and a double-linked list.
Hash table provides O(1) access to every element in the cache. Double-linked list allows you to move a node to the head or to the tail in O(1) time, although you'll need O(n) time locating this node. With their combiantion, both process can reach O(1) complexity.
The double-linked list is handwritten, while the hash table is unordered_map from new STL.
It's quite easy to make mistake on linked list, especially when it is double-linked.
Whenever set() or get() is called, the node visitied or created should be put on the front of the list.
Time complexity is O(1) for both set() and get(), linked list and hashing both use O(n) space.
Accepted code:
1 // 1WA, 1AC, double-linked list + unordered_map 2 #include <unordered_map> 3 using namespace std; 4 5 typedef struct DoubleLinkedListNode{ 6 public: 7 int key; 8 int value; 9 struct DoubleLinkedListNode *left; 10 struct DoubleLinkedListNode *right; 11 DoubleLinkedListNode(int _key = 0, int _value = 0): key(_key), value(_value), left(nullptr), right(nullptr) {} 12 }DoubleLinkedListNode; 13 14 class LRUCache{ 15 public: 16 LRUCache(int capacity) { 17 this->capacity = capacity; 18 this->size = 0; 19 this->head = this->tail = nullptr; 20 this->hash_table.clear(); 21 } 22 23 int get(int key) { 24 if (size == 0) { 25 // the LRU cache is empty. 26 return KEY_NOT_FOUND; 27 } 28 29 unordered_map<int, DoubleLinkedListNode *>::iterator mit; 30 31 mit = hash_table.find(key); 32 if (mit != hash_table.end()) { 33 // the key exists in the cache. 34 DoubleLinkedListNode *ptr = mit->second; 35 if (ptr == head) { 36 // do nothing 37 } else if (ptr == tail) { 38 // ptr is the tail node. 39 tail = ptr->left; 40 tail->right = nullptr; 41 ptr->left = nullptr; 42 ptr->right = head; 43 head->left = ptr; 44 head = ptr; 45 } else { 46 // ptr is at middle of the list. 47 DoubleLinkedListNode *ptr1, *ptr2; 48 ptr1 = ptr->left; 49 ptr2 = ptr->right; 50 ptr1->right = ptr2; 51 ptr2->left = ptr1; 52 ptr->left = nullptr; 53 ptr->right = head; 54 head->left = ptr; 55 head = ptr; 56 } 57 return ptr->value; 58 } else { 59 // key not found. 60 return KEY_NOT_FOUND; 61 } 62 } 63 64 void set(int key, int value) { 65 if (capacity == 0) { 66 // no room to place anything. 67 return; 68 } 69 70 unordered_map<int, DoubleLinkedListNode *>::iterator mit; 71 mit = hash_table.find(key); 72 if (mit != hash_table.end()) { 73 // the key exists in the cache. 74 DoubleLinkedListNode *ptr = mit->second; 75 if (ptr == head) { 76 // do nothing 77 } else if (ptr == tail) { 78 // ptr is the tail node. 79 tail = ptr->left; 80 tail->right = nullptr; 81 ptr->left = nullptr; 82 ptr->right = head; 83 head->left = ptr; 84 head = ptr; 85 } else { 86 // ptr is at middle of the list. 87 DoubleLinkedListNode *ptr1, *ptr2; 88 ptr1 = ptr->left; 89 ptr2 = ptr->right; 90 ptr1->right = ptr2; 91 ptr2->left = ptr1; 92 ptr->left = nullptr; 93 ptr->right = head; 94 head->left = ptr; 95 head = ptr; 96 } 97 ptr->value = value; 98 } else { 99 DoubleLinkedListNode *ptr = nullptr; 100 if (size < capacity) { 101 // still some space left. 102 // put the new item at front, not rear. 103 ptr = new DoubleLinkedListNode(key, value); 104 if (head != nullptr) { 105 head->left = ptr; 106 ptr->right = head; 107 } else { 108 tail = ptr; 109 } 110 head = ptr; 111 hash_table[key] = ptr; 112 ++size; 113 } else { 114 // capacity is reached. 115 ptr = tail; 116 hash_table.erase(tail->key); 117 ptr->key = key; 118 ptr->value = value; 119 if (head != tail) { 120 tail = ptr->left; 121 tail->right = nullptr; 122 ptr->left = nullptr; 123 ptr->right = head; 124 head->left = ptr; 125 head = ptr; 126 } 127 hash_table[key] = ptr; 128 } 129 } 130 } 131 private: 132 static const int KEY_NOT_FOUND = -1; 133 int capacity; 134 int size; 135 DoubleLinkedListNode *head; 136 DoubleLinkedListNode *tail; 137 unordered_map<int, DoubleLinkedListNode *> hash_table; 138 };