Back to LLD Problems: Parking Lot, Elevator, Chess, etc.
Q12Question 12 of 12

Design an Elevator System

Key Concepts
  • Each elevator car tracks its current floor, direction, and separate sorted request sets for up-stops and down-stops.
  • The SCAN/LOOK algorithm serves all requests in the current direction before reversing, preventing starvation.
  • An ElevatorController dispatches hall requests to the best car using a nearest-car or direction-compatible scoring policy.
  • Each car is an explicit state machine: IDLE, MOVING, DOORS_OPEN, DOORS_CLOSED, preventing unsafe boolean-flag race conditions.
  • A weight limit guard keeps doors open and fires an alert if boarding would exceed capacity.
  • The scheduling algorithm is a pluggable Strategy so the system can switch between nearest-car, SCAN, and LOOK without changing the controller.
  • In banking headquarters like National Bank of India, elevator design mirrors any safety-critical state machine with strict transition rules.

Definition

An elevator system dispatches hall requests to the best car and runs each car as a direction-aware state machine.
  • Elevator – a car with a current floor, direction (UP, DOWN, IDLE), and sorted request sets for each direction.
  • ElevatorController – receives external hall requests and dispatches them to the most suitable car using a scoring policy.
  • SCAN/LOOK algorithm – the car serves all stops in its current direction, then reverses, preventing starvation.
  • State machine – each car moves through IDLE, MOVING, DOORS_OPEN, and DOORS_CLOSED states with explicit transitions.
  • Weight limit – the car refuses to move while overloaded, keeping doors open and firing an alert.

Picture the multi-floor headquarters of National Bank of India. When Rajesh on floor 2 presses "up" and Priya on floor 8 presses "down", a smart controller decides which of several lifts should serve each person so nobody waits too long. Each lift is like a careful teller: it finishes the customers in its current direction before reversing, opens and closes its doors safely, and refuses to move if overloaded.

Understanding the Concepts

What are the core entities?

The core entities are Elevator, ElevatorController, Request, and Direction.

JAVA
package com.bank.elevator.model;

public enum Direction {
    UP, DOWN, IDLE
}
JAVA
package com.bank.elevator.model;

public class Request {
    private final int floor;
    private final Direction direction;

    public Request(int floor, Direction direction) {
        this.floor = floor;
        this.direction = direction;
    }

    public int getFloor() { return floor; }
    public Direction getDirection() { return direction; }
}

How does each elevator car work?

Each Elevator tracks its current floor, direction, and two sorted sets of stops: one for the up journey and one for the down journey.

JAVA
package com.bank.elevator.model;

import java.util.TreeSet;

public class Elevator {
    private final String carId;
    private int currentFloor;
    private Direction direction;
    private ElevatorState state;
    private final TreeSet<Integer> upStops;
    private final TreeSet<Integer> downStops;
    private final int maxWeight;
    private int currentWeight;

    public Elevator(String carId, int maxWeight) {
        this.carId = carId;
        this.currentFloor = 0;
        this.direction = Direction.IDLE;
        this.state = ElevatorState.IDLE;
        this.upStops = new TreeSet<>();
        this.downStops = new TreeSet<>();
        this.maxWeight = maxWeight;
        this.currentWeight = 0;
    }

    public void addStop(int floor) {
        if (floor > currentFloor) {
            upStops.add(floor);
        } else if (floor < currentFloor) {
            downStops.add(floor);
        }
    }

    public void move() {
        if (direction == Direction.UP && !upStops.isEmpty()) {
            currentFloor = upStops.pollFirst();
        } else if (direction == Direction.DOWN && !downStops.isEmpty()) {
            currentFloor = downStops.pollLast();
        }
        if (upStops.isEmpty() && downStops.isEmpty()) {
            direction = Direction.IDLE;
            state = ElevatorState.IDLE;
        } else if (upStops.isEmpty()) {
            direction = Direction.DOWN;
        } else if (downStops.isEmpty()) {
            direction = Direction.UP;
        }
        System.out.println("Car " + carId + " at floor " + currentFloor
                + ", direction: " + direction);
    }

    public boolean isOverweight(int additionalWeight) {
        return (currentWeight + additionalWeight) > maxWeight;
    }

    public int getCurrentFloor() { return currentFloor; }
    public Direction getDirection() { return direction; }
    public String getCarId() { return carId; }
    public ElevatorState getState() { return state; }
}

How does the SCAN/LOOK algorithm work?

The SCAN algorithm means the car keeps serving requests in its current direction until none remain, then reverses. This is efficient and avoids starvation, where a far-away request waits forever.

DIAGRAM
Car at floor 3, going UP, stops: {5, 7, 9}

  1. Move to 5 (serve stop)
  2. Move to 7 (serve stop)
  3. Move to 9 (serve stop)
  4. No more up stops → reverse to DOWN
  5. Serve down stops: {6, 2}
  6. Move to 6, then 2
  7. No more down stops → IDLE

How does the controller dispatch requests?

The ElevatorController receives external hall requests and picks the best car using a scoring policy.

JAVA
package com.bank.elevator.service;

import com.bank.elevator.model.*;
import java.util.List;

public class ElevatorController {
    private final List<Elevator> cars;

    public ElevatorController(List<Elevator> cars) {
        this.cars = cars;
    }

    public Elevator dispatch(Request request) {
        Elevator best = null;
        int bestScore = Integer.MAX_VALUE;

        for (Elevator car : cars) {
            int score = score(car, request);
            if (score < bestScore) {
                bestScore = score;
                best = car;
            }
        }
        if (best == null) {
            throw new IllegalStateException("No elevator available");
        }
        best.addStop(request.getFloor());
        System.out.println("Dispatched " + best.getCarId()
                + " to floor " + request.getFloor());
        return best;
    }

    private int score(Elevator car, Request request) {
        int distance = Math.abs(
                car.getCurrentFloor() - request.getFloor());
        // Prefer idle cars or cars already heading the right way
        if (car.getDirection() == Direction.IDLE) {
            return distance;
        }
        if (car.getDirection() == request.getDirection()
                && isOnTheWay(car, request)) {
            return distance;
        }
        return distance + 100; // penalise wrong direction
    }

    private boolean isOnTheWay(Elevator car, Request request) {
        if (car.getDirection() == Direction.UP) {
            return request.getFloor() > car.getCurrentFloor();
        }
        return request.getFloor() < car.getCurrentFloor();
    }
}

How is the elevator modelled as a state machine?

Each car is an explicit state machine to prevent unsafe boolean-flag race conditions.

JAVA
package com.bank.elevator.model;

public enum ElevatorState {
    IDLE,
    MOVING,
    DOORS_OPEN,
    DOORS_CLOSED
}
DIAGRAM
  IDLE ──request──→ DOORS_OPEN ──close──→ DOORS_CLOSED ──move──→ MOVING
    ▲                                                              │
    │                                                              │
    └──────────── no more stops ←── DOORS_OPEN ←── arrived ────────┘

The car refuses to move while doors are open. If boarding would exceed the weight limit, it keeps doors open and raises an alert rather than moving unsafely.

How do you handle weight limits?

The car tracks current weight and checks before closing doors.

JAVA
public void board(int passengerWeight) {
    if (isOverweight(passengerWeight)) {
        System.out.println("ALERT: Weight limit exceeded on "
                + carId + ". Doors remain open.");
        return;
    }
    currentWeight += passengerWeight;
}

What about extensibility?

The scheduling algorithm is a pluggable Strategy. The controller can switch between nearest-car, SCAN, and LOOK without changing its structure.

JAVA
package com.bank.elevator.service;

import com.bank.elevator.model.*;
import java.util.List;

public interface DispatchStrategy {
    Elevator selectCar(List<Elevator> cars, Request request);
}

What should a senior engineer know?

  • Model doors, weight checks, and motion as explicit states, not boolean flags. Implicit flags produce unsafe race conditions in real hardware control.
  • The SCAN algorithm prevents starvation. A simple nearest-car policy can starve far-away floors in a busy building.
  • In a real system, the controller runs on a dedicated thread and processes requests from a concurrent queue.
  • For multiple elevator banks (express, local), use separate controllers with different floor ranges.

What are common interview follow-up questions?

  • How would you handle a VIP express elevator that skips certain floors?
  • How do you prevent starvation of requests on distant floors?
  • What happens if an elevator is stuck between floors?
  • How would you add a maintenance mode for one car?
  • How do you scale to a building with 50 floors and 10 elevators?

Visual Overview

DIAGRAM
  Elevator State Machine:
  ───────────────────────

  IDLE ──request──→ DOORS_OPEN ──close──→ DOORS_CLOSED
    ▲                  ▲                      │
    │                  │                      │ move
    │              arrived                    ▼
    └── no stops ──── MOVING ←───────── DOORS_CLOSED
DIAGRAM
  SCAN Algorithm:
  ───────────────

  Car at floor 3, going UP

       9  ← stop
       7  ← stop
       5  ← stop
   >>> 3  (current)
       2  ← down stop

  Serves 5, 7, 9 (UP), then reverses, serves 2 (DOWN)
DIAGRAM
  Dispatch Scoring:
  ─────────────────

  Request: Floor 5, UP

  Car A: Floor 3, UP  → score = 2  (on the way) ← BEST
  Car B: Floor 8, DOWN → score = 103 (wrong direction)
  Car C: Floor 1, IDLE → score = 4  (idle, close)

Key Takeaway

When you design an elevator system, separate two concerns: the controller that decides which car serves a request, and each car that decides how to move. Each elevator car tracks its current floor, direction, and separate sorted sets of stops for the up and down journeys, and it runs as an explicit state machine moving between idle, moving, doors open, and doors closed.

For motion, the SCAN or LOOK algorithm means a car keeps serving every request in its current direction until there are none left, and only then reverses. This is efficient and prevents starvation, which matters in a busy building like National Bank of India headquarters where a far-away floor must never wait indefinitely.

For dispatch, the controller scores every car by distance to the request and direction compatibility. An idle car nearby or a car already heading toward the caller wins. A car heading the opposite direction is penalised. The scheduling algorithm is a pluggable Strategy so the system can switch policies without rewriting the controller.

Two safety concerns to always model explicitly are doors and weight. The car refuses to move while doors are open, and if boarding exceeds the weight limit it keeps doors open and raises an alert. The senior insight is that an elevator is fundamentally a safety-critical state machine, so designing the states and transitions first and treating scheduling as a pluggable strategy on top is the right decomposition.

Remember one sentence: an elevator system dispatches requests to the nearest direction-compatible car and runs each car as a strict state machine that never moves with doors open or weight exceeded.

---

Share
Use ← → arrow keys to navigate