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
DOWN
IDLE
ElevatorState
MOVING
STOPPED
IDLE
MAINTENANCE
STOPPED
IDLE
MAINTENANCE
DoorState
OPEN
CLOSED
OPENING
CLOSING
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.