LRU Cache - Complete Solution Guide
LRU Cache is LeetCode problem 146, a Medium level challenge. This complete guide provides step-by-step explanations, multiple solution approaches, and optimized code in python3, java, cpp, c.
Problem Statement
Design a data structure that follows the constraints of a Least Recently Used (LRU) cache . Implement the LRUCache class: LRUCache(int capacity) Initialize the LRU cache with positive size capacity . int get(int key) Return the value of the key if the key exists, otherwise return -1 . void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently u
Detailed Explanation
The problem requires designing and implementing an LRU (Least Recently Used) cache. An LRU cache is a data structure that stores key-value pairs and has a limited capacity. When the cache is full and a new key-value pair needs to be added, the least recently used item is evicted to make space. The `get` operation should return the value associated with a given key if it exists in the cache, otherwise return -1. The `put` operation should update the value of a key if it exists, or add a new key-value pair to the cache. Both `get` and `put` operations must have an average time complexity of O(1). The capacity of the cache is initialized during the creation of the `LRUCache` object.
Solution Approach
The solution uses a hash table (dictionary/unordered map) and a doubly-linked list (or similar ordered structure) to implement the LRU cache. The hash table stores key-value pairs, where the key is the cache key, and the value is the corresponding value or a pointer to the node in the linked list. The linked list maintains the order of keys based on their usage. The most recently used key is at the tail of the list, and the least recently used key is at the head. When a key is accessed (either through `get` or `put`), it's moved to the tail of the linked list. When a new key is added and the cache is full, the key at the head of the linked list is removed (LRU eviction).
Step-by-Step Algorithm
- Step 1: Initialize the LRUCache with a capacity and create a hash table and a doubly-linked list (or equivalent ordered structure).
- Step 2: Implement the `get` operation: If the key exists in the hash table, move the corresponding node to the end of the linked list and return the value. Otherwise, return -1.
- Step 3: Implement the `put` operation: If the key exists in the hash table, update the value and move the node to the end of the linked list. If the key doesn't exist, create a new node and add it to the end of the linked list. If the cache is full, remove the node at the head of the linked list and its corresponding entry in the hash table before adding the new node.
- Step 4: Maintain the doubly-linked list to reflect access order. Every time get or put is called, move the accessed node to the tail of the linked list. If the cache exceeds capacity during a put operation, remove the head node.
- Step 5: Ensure that the hash table and the linked list are synchronized. When a node is removed from the linked list, it should also be removed from the hash table, and vice versa.
Key Insights
- Insight 1: Using a hash table (unordered map, dictionary) allows O(1) average-case time complexity for `get` and `put` operations because we can quickly check if a key exists.
- Insight 2: A doubly-linked list (or an ordered dictionary like OrderedDict in Python or LinkedHashMap in Java) is used to keep track of the order in which keys were accessed. Moving a key to the tail (or end) of the list on every `get` and `put` operation ensures that the head (or beginning) of the list always contains the least recently used key.
- Insight 3: When `put` is called and the cache is full, the node at the head of the linked list (the LRU element) must be removed from both the linked list and the hash table to maintain O(1) complexity.
Complexity Analysis
Time Complexity: O(1)
Space Complexity: O(n)
Topics
This problem involves: Hash Table, Linked List, Design, Doubly-Linked List.
Companies
Asked at: AppFolio, Arista Networks, Aurora, Autodesk, BP, BitGo, Booking.com, ByteDance, Capital One, Chewy, Cisco, Citadel, Citrix, Cloudflare, Cohesity, Confluent, Coupang, Cruise, Disney, Docusign, DoorDash, Expedia, Fiverr, Freecharge, FreshWorks, General Motors, Goldman Sachs, Grab, Groww, Highspot, IBM, Intel, Intuit, J.P. Morgan, KLA, LinkedIn, Media.net, Mobileye, MongoDB, Morgan Stanley, Myntra, Navan, NetApp, Netflix, Niantic, Nokia, Nordstrom, Nutanix, Nvidia, Okta, Optiver, Oracle, Palantir Technologies, Palo Alto Networks, PayPal, PhonePe, Qualcomm, Rakuten, Reddit, Ripple, Rivian, Roku, Rubrik, SAP, Salesforce, Samsung, ServiceNow, Shopee, Shopify, Snap, Snowflake, SoFi, Splunk, Sprinklr, Squarespace, Swiggy, Tencent, Tesla, The Trade Desk, ThousandEyes, TikTok, Tripadvisor, Turo, Twitch, VMware, Verkada, Vimeo, Visa, Walmart Labs, Workday, X, Yahoo, Yandex, ZScaler, Zalando, Zenefits, Zepto, eBay, razorpay, smartnews, thoughtspot.