machine coding

Task Scheduler with Dependencies

A CI pipeline runner: priorities, dependencies that may arrive later, and cycle detection.

Hard120 minutesJava · class TaskScheduler

the brief

What you are building

Every git push starts a pipeline: compile, then unit tests and lint, then deploy, then notify the team.

Some jobs need others to finish first. Some are more urgent. Jobs can be added while the pipeline is already running — even before the jobs they depend on exist.

You will build the scheduler that always picks the right next job, and can warn when some jobs can never run.

must have

Requirements

  1. addTask(id, priority, dependsOn) adds a task. It returns false (and changes nothing) if this id was ever added before, even if it already ran.
  2. dependsOn may name tasks that are not added yet. A name repeated in dependsOn counts once. A dependency that already ran does not block.
  3. A task is ready when it has not run yet and every task it depends on has run.
  4. runNext() picks the ready task with the highest priority (equal priorities: the id that comes first alphabetically), marks it as run, and returns its id. If nothing is ready, it returns "".
  5. canFinishAll() returns true if every task added so far (and not run yet) could still run one day, by running ready tasks again and again. It returns false if some are stuck: in a cycle, depending on themselves, or waiting for a task that was never added. It changes nothing.

the contract

The Java API: class TaskScheduler

public TaskScheduler()
No tasks.
public boolean addTask(String id, int priority, String[] dependsOn)
false if id was added before.
public String runNext()
Runs the best ready task; returns its id, or "".
public boolean canFinishAll()
Could every unfinished task still run? Read-only.
  • up to 2,000 tasks and 10,000 calls in total
  • 0 ≤ dependsOn.length ≤ 10
  • priorities may be negative
  • ids are non-empty strings like unit-tests

example session

Calls in, answers out

A small build pipeline. notify is added before approval exists.

callreturnswhy
new TaskScheduler()—
addTask("compile", 5, new String[] {})true
addTask("unit-tests", 3, new String[] {"compile"})true
addTask("lint", 3, new String[] {})true
addTask("deploy", 9, new String[] {"unit-tests", "lint"})true
addTask("lint", 1, new String[] {})falselint was already added
runNext()"compile"deploy is most urgent but not ready; compile (5) beats lint (3)
runNext()"lint"lint and unit-tests tie at 3; "lint" comes first
canFinishAll()true
runNext()"unit-tests"
addTask("notify", 1, new String[] {"deploy", "approval"})trueapproval does not exist yet — allowed
canFinishAll()falsenotify waits for a task that was never added
runNext()"deploy"
runNext()""notify still waits for approval
addTask("approval", 2, new String[] {})true
canFinishAll()true
runNext()"approval"
runNext()"notify"
runNext()""all done

time plan

Your 120 minutes

  1. 15 minclarify — Can a dependency arrive later? Can an id be re-added after it ran? Tie rule? What exactly makes canFinishAll false?
  2. 15 mindesign — It is a graph: an edge from each dependency to the task waiting on it. Count unfinished dependencies per task; ready tasks go in a priority queue.
  3. 45 mincore code — addTask and runNext with the counts and the heap. Run the example up to deploy. Then canFinishAll with Kahn's algorithm on a copy.
  4. 30 minedge cases — A two-task cycle, a self-dependency, repeated dependencies, a dependency that already ran, a chain of 2,000 tasks added last-first.
  5. 15 minpolish — Be ready to run tasks on several workers in parallel, and to explain O(V + E).

Practise under real pressure: a 120-minute clock with the phases above. It keeps running if you reload the page.

your turn

Build it here

Write the whole class in the editor. Run replays the example session; Submit adds the hidden tests (edge cases and large random sessions). Stuck for 20 minutes? Open one hint at a time.

Build TaskScheduler in the browser

Design: implement every operation. Compiled by the real javac (Java 17) and judged right here — nothing is sent anywhere.

    The reference, the data-structure reasoning and the rubric are hidden so you can build it first. Even a half-working attempt teaches you more than reading a perfect answer.

    Opening all three hints also shows it.

    revise

    Chapters that teach these structures