Katkat

Design a Linked Queue with Deletion Follow-Ups

AmazonSoftware Engineer · Onsite

Last updated

Implement a linked-list-based FIFO queue supporting constant-time core operations, extended with value-based deletion methods that correctly maintain queue integrity and pointer boundaries.

Problem Statement

Design a First-In-First-Out (FIFO) queue backed by a linked list. The queue must support standard queue operations in O(1) time, as well as extended deletion methods that remove elements by value while preserving the relative ordering of the remaining elements.

Implement the LinkedQueue class with the following methods:

  • enqueue(val: int) -> None: Inserts val at the rear of the queue in O(1) time.
  • dequeue() -> int: Removes and returns the value at the front of the queue in O(1) time. Raises an IndexError if the queue is empty.
  • peek() -> int: Returns the value at the front without removing it. Raises an IndexError if the queue is empty.
  • is_empty() -> bool: Returns True if the queue has no elements, otherwise False.
  • delete_first(val: int) -> bool: Searches from front to back and removes the first occurrence of val. Returns True if an element was found and removed, or False if val does not exist in the queue.
  • delete_all(val: int) -> int: Removes all occurrences of val from the queue. Returns the total count of removed elements.

After any deletion, the queue's front (head), rear (tail), and size must remain consistent so subsequent enqueue and dequeue operations execute correctly.

Example

q = LinkedQueue()
q.enqueue(10)
q.enqueue(20)
q.enqueue(30)
q.enqueue(20)

print(q.delete_first(20)) # Output: True (removes first 20; queue is now 10 -> 30 -> 20)
print(q.peek())           # Output: 10
print(q.delete_all(20))   # Output: 1 (removes remaining 20; queue is now 10 -> 30)
print(q.dequeue())        # Output: 10
print(q.dequeue())        # Output: 30
print(q.is_empty())       # Output: True

Constraints

  • At most 10^5 total operations across all methods.
  • Node values are integers in the range [-10^9, 10^9].
  • enqueue, dequeue, peek, and is_empty must run in O(1) time.
  • delete_first and delete_all must run in O(n) time and O(1) auxiliary space, where n is the current number of elements.

Hints

Hint 1

Maintain both `head` (front) and `tail` (rear) pointers to ensure `enqueue` and `dequeue` run in O(1) time.

Hint 2

When deleting a node, take special care if the target node is the `head`, the `tail`, or the only remaining node in the queue.

Hint 3

For `delete_all`, iterate through the list using a `prev` pointer (or a dummy node) and update `tail` whenever the deleted node was the last element.

Follow-up questions

  • How would you redesign the data structure to support O(1) removal of an arbitrary node given a direct reference or unique key?
  • How would you make this queue thread-safe for multi-producer and multi-consumer workloads without bottlenecking on a single global lock?
  • What trade-offs arise if you implement deletion via lazy tombstone marking instead of immediate pointer manipulation?