class LRUCache: """ Our LRUCache class keeps track of the max number of nodes it can hold, the current number of nodes it is holding, a doubly- linked list that holds the key-value entries in the correct order, as well as a storage dict that provides fast access to every node stored in the cache. """ def __init__(self, limit=10): self.limit = limit self.size = 0 self.order = DoublyLinkedList() # self.order = list() self.storage = dict() """ Retrieves the value associated with the given key. Also needs to move the key-value pair to the end of the order such that the pair is considered most-recently used. Returns the value associated with the key or None if the key-value pair doesn't exist in the cache. """ def get(self, key): if key not in self.storage: return None else: node = self.storage[key] self.order.move_to_end(node) return node.value[1] """ Adds the given key-value pair to the cache. The newly- added pair should be considered the most-recently used entry in the cache. If the cache is already at max capacity before this entry is added, then the oldest entry in the cache needs to be removed to make room. Additionally, in the case that the key already exists in the cache, we simply want to overwrite the old value associated with the key with the newly-specified value. """ def set(self, key, value): if key in self.storage: node = self.storage[key] node.value = (key, value) self.order.move_to_end(node) return if len(self.order) == self.limit: evicted = self.order.head.value[0] del self.storage[evicted] self.order.remove_from_head() self.order.add_to_tail((key, value)) self.storage[key] = self.order.tail
class DoublyLinkedListTests(unittest.TestCase): def setUp(self): self.node = ListNode(1) self.dll = DoublyLinkedList(self.node) def test_list_remove_from_tail(self): self.dll.remove_from_tail() self.assertIsNone(self.dll.head) self.assertIsNone(self.dll.tail) self.assertEqual(len(self.dll), 0) self.dll.add_to_tail(33) self.assertEqual(self.dll.head.value, 33) self.assertEqual(self.dll.tail.value, 33) self.assertEqual(len(self.dll), 1) self.assertEqual(self.dll.remove_from_tail(), 33) self.assertEqual(len(self.dll), 0) self.dll.add_to_tail(68) self.assertEqual(len(self.dll), 1) self.assertEqual(self.dll.remove_from_tail(), 68) self.assertEqual(len(self.dll), 0) def test_list_remove_from_head(self): self.dll.remove_from_head() self.assertIsNone(self.dll.head) self.assertIsNone(self.dll.tail) self.assertEqual(len(self.dll), 0) self.dll.add_to_head(2) self.assertEqual(self.dll.head.value, 2) self.assertEqual(self.dll.tail.value, 2) self.assertEqual(len(self.dll), 1) self.assertEqual(self.dll.remove_from_head(), 2) self.assertEqual(len(self.dll), 0) self.dll.add_to_head(55) self.assertEqual(len(self.dll), 1) self.assertEqual(self.dll.remove_from_head(), 55) self.assertEqual(len(self.dll), 0) def test_list_add_to_tail(self): self.assertEqual(self.dll.tail.value, 1) self.assertEqual(len(self.dll), 1) self.dll.add_to_tail(30) self.assertEqual(self.dll.tail.prev.value, 1) self.assertEqual(self.dll.tail.value, 30) self.assertEqual(len(self.dll), 2) self.dll.add_to_tail(20) self.assertEqual(self.dll.tail.prev.value, 30) self.assertEqual(self.dll.tail.value, 20) self.assertEqual(len(self.dll), 3) def test_node_delete(self): node_1 = ListNode(3) node_2 = ListNode(4) node_3 = ListNode(5) node_1.next = node_2 node_2.next = node_3 node_2.prev = node_1 node_3.prev = node_2 node_2.delete() self.assertEqual(node_1.next, node_3) self.assertEqual(node_3.prev, node_1) def test_node_insert_before(self): self.node.insert_before(0) self.assertEqual(self.node.prev.value, 0) def test_list_add_to_head(self): self.assertEqual(self.dll.head.value, 1) self.dll.add_to_head(10) self.assertEqual(self.dll.head.value, 10) self.assertEqual(self.dll.head.next.value, 1) self.assertEqual(len(self.dll), 2) def test_node_insert_after(self): self.node.insert_after(2) self.assertEqual(self.node.next.value, 2) def test_list_move_to_end(self): self.dll.add_to_head(40) self.assertEqual(self.dll.tail.value, 1) self.assertEqual(self.dll.head.value, 40) self.dll.move_to_end(self.dll.head) self.assertEqual(self.dll.tail.value, 40) self.assertEqual(self.dll.tail.prev.value, 1) self.assertEqual(len(self.dll), 2) self.dll.add_to_tail(4) self.dll.move_to_end(self.dll.head.next) self.assertEqual(self.dll.tail.value, 40) self.assertEqual(self.dll.tail.prev.value, 4) self.assertEqual(len(self.dll), 3) def test_list_move_to_front(self): self.dll.add_to_tail(3) self.assertEqual(self.dll.head.value, 1) self.assertEqual(self.dll.tail.value, 3) self.dll.move_to_front(self.dll.tail) self.assertEqual(self.dll.head.value, 3) self.assertEqual(self.dll.head.next.value, 1) self.assertEqual(len(self.dll), 2) self.dll.add_to_head(29) self.dll.move_to_front(self.dll.head.next) self.assertEqual(self.dll.head.value, 3) self.assertEqual(self.dll.head.next.value, 29) self.assertEqual(len(self.dll), 3) def test_list_delete(self): self.dll.delete(self.node) self.assertIsNone(self.dll.head) self.assertIsNone(self.dll.tail) self.assertEqual(len(self.dll), 0) self.dll.add_to_tail(1) self.dll.add_to_head(9) self.dll.add_to_tail(6) self.dll.delete(self.dll.head) self.assertEqual(self.dll.head.value, 1) self.assertEqual(self.dll.tail.value, 6) self.assertEqual(len(self.dll), 2) self.dll.delete(self.dll.head) self.assertEqual(self.dll.head.value, 6) self.assertEqual(self.dll.tail.value, 6) self.assertEqual(len(self.dll), 1) def test_get_max(self): self.assertEqual(self.dll.get_max(), 1) self.dll.add_to_tail(100) self.assertEqual(self.dll.get_max(), 100) self.dll.add_to_tail(55) self.assertEqual(self.dll.get_max(), 100) self.dll.add_to_tail(101) self.assertEqual(self.dll.get_max(), 101)