Least Recently Used Cache - Amazon Top Interview Questions


Problem Statement :


Implement a least recently used cache with the following methods:

LRUCache(int capacity) constructs a new instance of a LRU cache with the given capacity.
get(int key) retrieves the value associated with the key key. If it does not exist, return -1. As a side effect, this key now becomes the most recently used key.
set(int key, int val) updates the key key with value val. If updating this key-value pair exceeds capacity, then evicts the least recently used key-value pair.
Each method should be done in \mathcal{O}(1)O(1) time.

Constraints


n ≤ 100,000 where n is the number of calls to get and set.

Example 1

Input

methods = ["constructor", "set", "get", "set", "set", "set", "get", "get"]
arguments = [[3], [1, 10], [1], [2, 20], [3, 30], [4, 40], [1], [4]]`

Output

[None, None, 10, None, None, None, -1, 40]

Explanation

We create an LRUCache of capacity 3.

Set key of 1 to value 10. Size of cache is now 1
We get 1 which has value of 10
Set key of 2 to value 20. Size of cache is now 2
Set key of 3 to value 30. Size of cache is now 3
Set key of 4 to value 40. Size exceeds capacity, so now we evict the least recently used key which is 1.
We get 1 which has been evicted so we return -1
We get 4 which has value of 40



Solution :



title-img




                        Solution in C++ :

class LRUCache {
    public:
    LRUCache(int capacity) : cap(capacity) {
    }

    int get(int key) {
        auto it = m.find(key);
        if (it == m.end()) return -1;
        auto lit = it->second;
        int val = lit->second;
        lst.erase(lit);
        lst.push_front({key, val});
        it->second = lst.begin();
        return val;
    }

    void set(int key, int val) {
        auto it = m.find(key);
        if (it == m.end()) {
            lst.push_front({key, val});
            m.insert({key, lst.begin()});
        } else {
            auto lit = it->second;
            lst.erase(lit);
            lst.push_front({key, val});
            it->second = lst.begin();
        }

        if (lst.size() > cap) {
            auto p = lst.back();
            m.erase(p.first);
            lst.pop_back();
        }
    }

    private:
    int cap;
    list<pair<int, int>> lst;
    unordered_map<int, list<pair<int, int>>::iterator> m;
};
                    




                        Solution in Python : 
                            
class DLLNode:
    def __init__(self, key, val):
        self.key = key
        self.val = val
        self.next = None
        self.prev = None


class DLL:
    def __init__(self):
        """
        initialize Doubly Linked List with 2 dummy nodes to
        get first and last nodes in O(1) time because our operations
        require us to either push to the front or pop from the end.
        """
        self.head = DLLNode(0, 0)
        self.tail = DLLNode(0, 0)
        self.head.next, self.tail.prev = self.tail, self.head
        self.size = 0

    def delete(self, node):
        """
        Delete a DLL node, given its pointer. we just update the
        2 pointers that point to this node and we're done
        """
        node.next.prev, node.prev.next = node.prev, node.next
        self.size -= 1

    def push_front(self, node):
        """
        since we already have the pointer to the head of DLL, we just push this
        node to the next of the head
        """
        node.next = self.head.next
        node.prev = self.head
        self.head.next.prev = node
        self.head.next = node
        self.size += 1

    def get_last(self):
        return self.tail.prev


class LRUCache:
    def __init__(self, capacity):
        self.dll = DLL()
        self.mapping = {}
        self.capacity = capacity

    def get(self, key):
        """
        If the key is not present in mapping, we simply return -1.
        Else we get the pointer to that node in the DLL and move the node
        to the front and return its value
        """
        if key not in self.mapping:
            return -1
        node = self.mapping[key]

        # now move this node to the front of the array
        self.dll.delete(node)
        self.dll.push_front(node)

        return node.val

    def set(self, key, val):
        """
        If key already has a value set, we get the DLL node and delete it.
        We don't remove from the mapping since we will overwrite it anyway.

        then we create a new node and insert to the front of the DLL and update
        the mapping with this new node.

        If the size is more than the capacity, we get the last element and pop it
        """
        if key in self.mapping:
            node = self.mapping[key]
            self.dll.delete(node)

        # add a new entry to the front
        node = DLLNode(key, val)
        self.dll.push_front(node)
        self.mapping[key] = node

        # if the size is more than the capacity, pop last
        if len(self.mapping) > self.capacity:
            last_node = self.dll.get_last()
            self.dll.delete(last_node)
            del self.mapping[last_node.key]
                    


View More Similar Problems

QHEAP1

This question is designed to help you get a better understanding of basic heap operations. You will be given queries of types: " 1 v " - Add an element to the heap. " 2 v " - Delete the element from the heap. "3" - Print the minimum of all the elements in the heap. NOTE: It is guaranteed that the element to be deleted will be there in the heap. Also, at any instant, only distinct element

View Solution →

Jesse and Cookies

Jesse loves cookies. He wants the sweetness of all his cookies to be greater than value K. To do this, Jesse repeatedly mixes two cookies with the least sweetness. He creates a special combined cookie with: sweetness Least sweet cookie 2nd least sweet cookie). He repeats this procedure until all the cookies in his collection have a sweetness > = K. You are given Jesse's cookies. Print t

View Solution →

Find the Running Median

The median of a set of integers is the midpoint value of the data set for which an equal number of integers are less than and greater than the value. To find the median, you must first sort your set of integers in non-decreasing order, then: If your set contains an odd number of elements, the median is the middle element of the sorted sample. In the sorted set { 1, 2, 3 } , 2 is the median.

View Solution →

Minimum Average Waiting Time

Tieu owns a pizza restaurant and he manages it in his own way. While in a normal restaurant, a customer is served by following the first-come, first-served rule, Tieu simply minimizes the average waiting time of his customers. So he gets to decide who is served first, regardless of how sooner or later a person comes. Different kinds of pizzas take different amounts of time to cook. Also, once h

View Solution →

Merging Communities

People connect with each other in a social network. A connection between Person I and Person J is represented as . When two persons belonging to different communities connect, the net effect is the merger of both communities which I and J belongs to. At the beginning, there are N people representing N communities. Suppose person 1 and 2 connected and later 2 and 3 connected, then ,1 , 2 and 3 w

View Solution →

Components in a graph

There are 2 * N nodes in an undirected graph, and a number of edges connecting some nodes. In each edge, the first value will be between 1 and N, inclusive. The second node will be between N + 1 and , 2 * N inclusive. Given a list of edges, determine the size of the smallest and largest connected components that have or more nodes. A node can have any number of connections. The highest node valu

View Solution →