A classic object-design question: model the lot, the spots, the cars, and the rules that decide who parks where and what they owe.
A parking lot is a tidy little world of objects. A ParkingLot holds one or more Levels; each level holds many Spots, and every spot has a size — motorcycle, compact, large, or handicapped. A Vehicle arriving at the gate needs the nearest free spot it actually fits into.
When a car parks, the system issues a Ticket recording the spot and the entry time. On the way out, the ticket's duration sets the fee. The whole interview is really about three things: the entities and how they relate, the policy for assigning a spot, and doing it safely when many cars arrive at once.
Each level keeps its spots in order so it can scan for the first free spot whose size fits the vehicle. A motorcycle fits anywhere; a large vehicle needs a large (or handicapped) spot. The assignment runs under a lock so two cars can never claim the same spot.
SIZE_RANK = {"motorcycle": 0, "compact": 1, "large": 2}
class Vehicle:
def __init__(self, size):
self.size = size # "motorcycle" | "compact" | "large"
class ParkingSpot:
def __init__(self, spot_id, size):
self.id = spot_id
self.size = size
self.is_free = True
def fits(self, vehicle):
# a vehicle fits a spot at least as big as itself
return SIZE_RANK[vehicle.size] <= SIZE_RANK[self.size]
class Level:
def __init__(self, spots):
self.spots = spots # ordered nearest -> farthest
def find_spot(self, vehicle):
for spot in self.spots: # first-fit, nearest first
if spot.is_free and spot.fits(vehicle):
return spot
return None
class Ticket:
def __init__(self, spot, entry_time):
self.spot = spot
self.entry_time = entry_time
class ParkingLot:
def __init__(self, levels, rate=3):
self.levels = levels
self.rate = rate # dollars per hour
self.lock = Lock()
self.tickets = {}
def park(self, vehicle, now):
with self.lock: # atomic: no double-booking
for level in self.levels:
spot = level.find_spot(vehicle)
if spot:
spot.is_free = False
t = Ticket(spot, now)
self.tickets[id(t)] = t
return t
return None # lot full for this size
def leave(self, ticket, now):
hours = ceil((now - ticket.entry_time) / 3600)
fee = max(1, hours) * self.rate
ticket.spot.is_free = True
return fee
The find_spot loop is first-fit: because spots are stored nearest-first, the first match is also the nearest. The fee rounds partial hours up with ceil, so 2.5 hours bills as 3.
| Decision | Option | Why you'd pick it |
|---|---|---|
| Assignment policy | First-fit (nearest) | Simple and fast; spots stored in walking order, so the first match is also the closest. |
| Assignment policy | Best-fit (smallest that fits) | Keeps large spots free for large cars, but costs a full scan and can strand cars far away. |
| Concurrency | Single global lock | Trivially correct, but every gate serializes through one lock — a throughput ceiling. |
| Concurrency | Per-level lock or atomic spot flag | Far more parallelism; harder to reason about and to keep counters consistent. |
| Occupancy store | In-memory map | Microsecond reads, dead simple — but state is lost on restart and can't scale past one node. |
| Occupancy store | Database row per spot | Durable and shareable across nodes; needs transactions and pays network latency per park. |
is_free, or a conditional database update.park must clearly return "no spot" rather than crash or hand back a stale spot — the gate should turn the car away.Take a small lot: 4 compact spots (C1–C4) and 2 large spots (L1–L2), all free, rate $3/hour. Four vehicles arrive in order.
Motorcycle — first-fit scans C1 first; a motorcycle fits a compact spot, so it parks at C1 and gets a ticket. Car A (compact) takes the next free fitting spot, C2. Car B (compact) takes C3. Truck (large) needs a large spot — compacts don't fit — so the scan skips C1–C4 and parks it at L1.
Now Car A leaves after 2.5 hours. The fee rounds the duration up: ceil(2.5) = 3 hours, so 3 × $3 = $9. Spot C2 flips back to free, and the next compact arrival can reuse it. The rounding rule is the thing to say out loud: any started hour is a full billed hour.
A large truck arrives and the only free spots left are two compact ones. What should park do?
Two cars hit the gate at the same instant and both see spot C1 free. What keeps them from both parking there?