LLD Problem

Design Elevator System

Design an elevator system for a building with multiple elevators. Handle request scheduling, direction optimization, and state management.

25 min readClassic LLD Problem

1Requirements Gathering

Functional Requirements
  • Multiple elevators in a building
  • Handle floor requests (from outside)
  • Handle destination requests (from inside)
  • Move between floors efficiently
  • Open/close doors at each stop
  • Display current floor and direction
Questions to Ask
  • How many floors? How many elevators?
  • What scheduling algorithm?
  • Emergency stop functionality?
  • Priority floors (VIP, service)?
  • Weight capacity?
  • Fire emergency mode?

2Class Design

Core Classes

Singleton
ElevatorSystem
- elevators[]
- scheduler
+ requestElevator()
+ addElevator()
Class
Elevator
- id
- currentFloor
- direction
- state
- requests
+ move()
+ addRequest()
+ openDoor()
+ closeDoor()
Interface
ElevatorScheduler
+ selectElevator()
+ calculateCost()
Class
Request
- sourceFloor
- destFloor
- direction
- timestamp
+ getSourceFloor()
+ getDestFloor()
Abstract
Button
- floor
+ press()
Class
Display
- floor
- direction
+ update()

Enums

Direction
UP
DOWN
IDLE
ElevatorState
MOVING
STOPPED
IDLE
MAINTENANCE
DoorState
OPEN
CLOSED
OPENING
CLOSING

3Scheduling Algorithms

FCFS (First Come First Serve)

Simplest - serve requests in order received.

  • + Simple to implement
  • - Inefficient, lots of direction changes
SSTF (Shortest Seek Time First)

Pick closest request to current position.

  • + Minimizes movement
  • - Can starve far requests
SCAN (Elevator Algorithm)

Go in one direction until end, then reverse.

  • + Fair, no starvation
  • + Most common in real elevators
LOOK

Like SCAN but reverses at last request, not end.

  • + More efficient than SCAN
  • + Used in this implementation

4Implementation

Elevator System Implementation
// ========== Enums ==========
enum Direction { UP, DOWN, IDLE }
enum ElevatorState { MOVING, STOPPED, IDLE, MAINTENANCE }

// ========== Elevator ==========
class Elevator {
    private int id;
    private int currentFloor;
    private Direction direction;
    private ElevatorState state;
    private TreeSet<Integer> upRequests;    // Floors to visit going up
    private TreeSet<Integer> downRequests;  // Floors to visit going down
    
    public Elevator(int id) {
        this.id = id;
        this.currentFloor = 0;
        this.direction = Direction.IDLE;
        this.state = ElevatorState.IDLE;
        this.upRequests = new TreeSet<>();
        this.downRequests = new TreeSet<>(Collections.reverseOrder());
    }
    
    public void addRequest(int floor) {
        if (floor > currentFloor) {
            upRequests.add(floor);
        } else if (floor < currentFloor) {
            downRequests.add(floor);
        }
        if (state == ElevatorState.IDLE) {
            direction = floor > currentFloor ? Direction.UP : Direction.DOWN;
        }
    }
    
    public void move() {
        if (direction == Direction.UP) {
            if (!upRequests.isEmpty()) {
                int next = upRequests.first();
                moveToFloor(next);
                upRequests.remove(next);
            } else if (!downRequests.isEmpty()) {
                direction = Direction.DOWN;
            } else {
                direction = Direction.IDLE;
                state = ElevatorState.IDLE;
            }
        } else if (direction == Direction.DOWN) {
            if (!downRequests.isEmpty()) {
                int next = downRequests.first();
                moveToFloor(next);
                downRequests.remove(next);
            } else if (!upRequests.isEmpty()) {
                direction = Direction.UP;
            } else {
                direction = Direction.IDLE;
                state = ElevatorState.IDLE;
            }
        }
    }
    
    private void moveToFloor(int floor) {
        state = ElevatorState.MOVING;
        System.out.println("Elevator " + id + ": " + currentFloor + " -> " + floor);
        currentFloor = floor;
        state = ElevatorState.STOPPED;
        openDoor();
    }
    
    private void openDoor() {
        System.out.println("Elevator " + id + ": Door open at floor " + currentFloor);
    }
    
    public int getCurrentFloor() { return currentFloor; }
    public Direction getDirection() { return direction; }
    public boolean isIdle() { return state == ElevatorState.IDLE; }
    
    // Cost for scheduler (lower is better)
    public int getCost(int floor, Direction requestDir) {
        int distance = Math.abs(floor - currentFloor);
        if (direction == Direction.IDLE) return distance;
        if (direction == requestDir) {
            if ((direction == Direction.UP && floor >= currentFloor) ||
                (direction == Direction.DOWN && floor <= currentFloor)) {
                return distance;
            }
        }
        return distance + 10; // Penalty for opposite direction
    }
}

// ========== Scheduler ==========
interface ElevatorScheduler {
    Elevator selectElevator(List<Elevator> elevators, int floor, Direction dir);
}

class LookScheduler implements ElevatorScheduler {
    @Override
    public Elevator selectElevator(List<Elevator> elevators, int floor, Direction dir) {
        return elevators.stream()
            .min(Comparator.comparingInt(e -> e.getCost(floor, dir)))
            .orElse(elevators.get(0));
    }
}

// ========== System ==========
class ElevatorSystem {
    private static ElevatorSystem instance;
    private List<Elevator> elevators;
    private ElevatorScheduler scheduler;
    
    private ElevatorSystem(int numElevators) {
        elevators = new ArrayList<>();
        for (int i = 0; i < numElevators; i++) {
            elevators.add(new Elevator(i));
        }
        scheduler = new LookScheduler();
    }
    
    public static ElevatorSystem getInstance(int n) {
        if (instance == null) instance = new ElevatorSystem(n);
        return instance;
    }
    
    public void requestElevator(int floor, Direction direction) {
        Elevator best = scheduler.selectElevator(elevators, floor, direction);
        best.addRequest(floor);
        System.out.println("Request floor " + floor + " assigned to Elevator " + 
            elevators.indexOf(best));
    }
    
    public void step() {
        for (Elevator e : elevators) {
            e.move();
        }
    }
}

5Interview Follow-up Questions

Interview Follow-up Questions

Common follow-up questions interviewers ask

6Key Takeaways

1Use Singleton for ElevatorSystem, Strategy for scheduling.
2LOOK algorithm is most commonly used - efficient and fair.
3Separate upRequests and downRequests for easy direction management.
4Consider emergency scenarios and edge cases.
5Discuss optimizations: predictive positioning, zone allocation.
6Thread safety matters if handling concurrent requests.