Katkat

Implement a Reusable Locker Allocation System

GoogleSoftware Engineer · Technical Screen

Last updated

Design and implement a locker bank management system that assigns the lowest available locker index, handles invalid store and pickup operations, releases lockers immediately, and tracks the last package successfully stored in each locker.

Problem Statement

Design a locker management system for an automated delivery kiosk. The kiosk contains a fixed number of lockers numbered 1 through capacity.

Implement the LockerSystem class with the following methods:

  • __init__(self, capacity: int): Initializes the locker bank with capacity lockers, indexed from 1 to capacity. Initially, all lockers are empty.
  • store(self, package_id: str) -> int: Assigns the lowest-numbered available locker to hold package_id. If all lockers are occupied or if package_id is already currently stored in another locker, the operation is invalid and must return -1. Otherwise, returns the assigned locker_id.
  • pickup(self, locker_id: int, package_id: str) -> bool: Releases the specified locker if locker_id is valid, is currently occupied, and contains package_id. If released, the locker immediately becomes available for reuse by future store operations, and the method returns True. If locker_id is invalid, not currently occupied, or the stored package does not match package_id, the operation fails and returns False.
  • get_last_stored(self, locker_id: int) -> str | None: Returns the package_id of the package most recently stored in locker_id, even if the locker is currently empty. If locker_id is invalid or no package has ever been stored in that locker, returns None.

Example

lockers = LockerSystem(3)

lockers.store("PKG-A")        # returns 1 (locker 1 is lowest available)
lockers.store("PKG-B")        # returns 2 (locker 2 is lowest available)
lockers.store("PKG-A")        # returns -1 (PKG-A is already in the system)

lockers.pickup(1, "PKG-A")    # returns True (locker 1 is now empty)
lockers.pickup(1, "PKG-A")    # returns False (locker 1 is already empty)

lockers.store("PKG-C")        # returns 1 (locker 1 was released and is lowest available)

lockers.get_last_stored(1)    # returns "PKG-C"
lockers.pickup(1, "PKG-C")    # returns True
lockers.get_last_stored(1)    # returns "PKG-C" (retained after pickup)
lockers.get_last_stored(3)    # returns None (locker 3 never had a package)

Constraints

  • 1 <= capacity <= 10^5
  • 1 <= len(package_id) <= 32, containing alphanumeric characters only.
  • At most 2 * 10^5 total calls across store, pickup, and get_last_stored.

Edge Cases to Consider

  • Attempting to store when the locker bank is full.
  • Attempting to store an item whose package_id is currently in another locker versus one that was previously picked up.
  • Invalid locker_id values (e.g., capacity).
  • Querying get_last_stored before any item has been stored in a locker.

Hints

Hint 1

To always allocate the lowest-numbered locker efficiently, consider using a min-heap initialized with all locker IDs from 1 to capacity.

Hint 2

Maintain hash maps to map package IDs to their active lockers and locker IDs to their active packages so that store and pickup checks run in O(1) time.

Hint 3

Use a separate array or hash map that records the last stored package ID for each locker, updating it only upon successful store operations and never clearing it on pickup.

Follow-up questions

  • How would you optimize initialization time and memory if capacity is 10^9 but at most 10^5 operations are executed?
  • How would you extend this system to support locker sizes (small, medium, large) where a package can fit into its designated size or any larger size?
  • How would you ensure thread-safety if multiple pickup and store requests occur concurrently across multiple worker processes?