Design a special queue data structure that maintains elements in order of their most recent access. The queue should support two primary operations:
The queue is initialized with a capacity parameter n, though the actual number of elements may vary based on operations performed.
The key challenge is implementing the fetch operation efficiently. When you fetch an element at position k, you must return its value and reposition it to the end of the queue, shifting other elements as needed.
MRUQueue(n) that initializes the queue with capacity nenqueue(val) to add a value to the end of the queuefetch(k) to return the element at position k (1-indexed) and move it to the endfetch(k) operation, the fetched element should be at the most recent position (end)Example 1:
` Operations: MRUQueue mru = new MRUQueue(5) mru.enqueue(10) mru.enqueue(20) mru.enqueue(30) mru.fetch(2)
Output: 20 Explanation:
Example 2:
` Operations: MRUQueue mru = new MRUQueue(3) mru.enqueue(5) mru.enqueue(10) mru.enqueue(15) mru.fetch(3) mru.fetch(2)
Output: fetch(3) returns 15, fetch(2) returns 10 Explanation:
Example 3:
` Operations: MRUQueue mru = new MRUQueue(4) mru.enqueue(1) mru.enqueue(2) mru.enqueue(3) mru.fetch(1) mru.enqueue(4) mru.fetch(2)
Output: fetch(1) returns 1, fetch(2) returns 3 Explanation:
Hint 1: Data Structure Choice The simplest approach uses a list/array to store elements. While not optimal for very large inputs, it provides straightforward implementation and is acceptable given the constraint limits. The fetch operation requires removing an element from the middle and appending it to the end.
Hint 2: Array Implementation For the array approach, you can use
pop(index)to remove the element at position k-1 (converting from 1-indexed to 0-indexed) andappend()to add it to the end. This gives O(n) time for fetch operations but O(1) for enqueue, which is acceptable for the given constraints.
Hint 3: Optimization Consideration For a more optimized solution handling many fetch operations on large queues, consider using a data structure that supports efficient removal from arbitrary positions, such as a combination of a linked list for ordering and a hash map for quick access. However, for the given constraints (n ≤ 2000), a simple array is sufficient.
Full Solution ` class MRUQueue: def init(self, n: int): """ Initialize the queue with capacity n. We use a simple list to store elements. """ self.queue = []
def enqueue(self, val: int) -> None: """ Add a value to the end of the queue. Time Complexity: O(1) """ self.queue.append(val) def fetch(self, k: int) -> int: """ Retrieve the element at position k (1-indexed), then move it to the most recent position (end of queue). Time Complexity: O(n) due to list removal from middle """ # Convert to 0-indexed index = k - 1 # Get the value at position k val = self.queue[index] # Remove it from its current position self.queue.pop(index) # Add it to the end (most recent position) self.queue.append(val) return val`
Approach Explanation:
The solution uses a straightforward list-based implementation:
Initialization: Store elements in a list that represents the queue in access order
Enqueue: Simply append to the end of the list - O(1) operation
Fetch:
- Convert the 1-indexed position to 0-indexed
- Retrieve the value at that position
- Remove it from the current position using
pop(index)- O(n) operation- Append it to the end to make it most recently accessed - O(1) operation
- Return the value
Time Complexity:
enqueue: O(1) - appending to a listfetch: O(n) - removing from middle of list requires shifting elementsSpace Complexity: O(n) where n is the number of elements in the queue
Alternative Optimization:
For scenarios with many fetch operations, you could optimize using:
- A doubly-linked list for O(1) removal and insertion
- A hash map to store node references for O(1) lookup
- This would make fetch O(1) but adds complexity
However, given the constraints (at most 2000 operations, n ≤ 2000), the simple array solution is both correct and efficient enough.