Card 06/ 08

CodeDifficulty: Intermediate4 min

Greedy Scheduling in Python, Java and TypeScript

Sort by end time, then keep each meeting that starts after the last one kept.

Greedy scheduling is a sort with a key followed by one loop, and the listings below give two of them: the most meetings in one room, sorted by end time, and the smallest worst lateness, sorted by deadline. Each runs the six meetings from Schedule the Most Meetings by Earliest Finish and the four jobs from Choosing the Key a Greedy Algorithm Sorts By.

Two greedy schedules, each a sort by one key and a single scan

greedy_code.pypy
def most_meetings(meetings: list[tuple[int, int]]) -> list[tuple[int, int]]:
    kept: list[tuple[int, int]] = []
    for start, end in sorted(meetings, key=lambda meeting: meeting[1]):
        if not kept or start >= kept[-1][1]:
            kept.append((start, end))
    return kept


def least_lateness(jobs: dict[str, tuple[int, int]]) -> tuple[list[str], int]:
    order = sorted(jobs, key=lambda name: jobs[name][1])
    time, worst = 0, 0
    for name in order:
        duration, deadline = jobs[name]
        time += duration
        worst = max(worst, time - deadline)
    return order, worst


meetings = [(1, 3), (2, 5), (4, 7), (6, 8), (5, 9), (8, 10)]
print("meetings kept:", " ".join(f"({start}, {end})" for start, end in most_meetings(meetings)))
order, worst = least_lateness({"A": (3, 3), "B": (1, 6), "C": (2, 8), "D": (4, 9)})
print(f"jobs by deadline: {' '.join(order)}, worst lateness {worst}")
OutputRan with python:3.14.8-slim-trixie (Python 3.14.8)
meetings kept: (1, 3) (4, 7) (8, 10)
jobs by deadline: A B C D, worst lateness 1
GreedyCode.javajava
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class GreedyCode {
    record Meeting(int start, int end) {}

    record Job(int duration, int deadline) {}

    static List<Meeting> mostMeetings(List<Meeting> meetings) {
        List<Meeting> sorted = new ArrayList<>(meetings);
        sorted.sort(Comparator.comparingInt(Meeting::end));
        List<Meeting> kept = new ArrayList<>();
        for (Meeting meeting : sorted) {
            if (kept.isEmpty() || meeting.start() >= kept.get(kept.size() - 1).end()) {
                kept.add(meeting);
            }
        }
        return kept;
    }

    static int leastLateness(Map<String, Job> jobs, List<String> order) {
        order.addAll(jobs.keySet());
        order.sort(Comparator.comparingInt(name -> jobs.get(name).deadline()));
        int time = 0;
        int worst = 0;
        for (String name : order) {
            time += jobs.get(name).duration();
            worst = Math.max(worst, time - jobs.get(name).deadline());
        }
        return worst;
    }

    public static void main(String[] args) {
        List<Meeting> meetings = List.of(new Meeting(1, 3), new Meeting(2, 5), new Meeting(4, 7),
                new Meeting(6, 8), new Meeting(5, 9), new Meeting(8, 10));
        System.out.println("meetings kept: " + mostMeetings(meetings).stream()
                .map(m -> "(" + m.start() + ", " + m.end() + ")").collect(Collectors.joining(" ")));
        Map<String, Job> jobs = new LinkedHashMap<>();
        jobs.put("A", new Job(3, 3));
        jobs.put("B", new Job(1, 6));
        jobs.put("C", new Job(2, 8));
        jobs.put("D", new Job(4, 9));
        List<String> order = new ArrayList<>();
        int worst = leastLateness(jobs, order);
        System.out.println("jobs by deadline: " + String.join(" ", order) + ", worst lateness " + worst);
    }
}
OutputRan with eclipse-temurin:25.0.4.1_1-jdk-noble (Java 25.0.4.1)
meetings kept: (1, 3) (4, 7) (8, 10)
jobs by deadline: A B C D, worst lateness 1
greedy_code.tsts
type Meeting = [start: number, end: number];

function mostMeetings(meetings: Meeting[]): Meeting[] {
  const kept: Meeting[] = [];
  for (const [start, end] of [...meetings].sort((a, b) => a[1] - b[1])) {
    if (kept.length === 0 || start >= kept[kept.length - 1][1]) {
      kept.push([start, end]);
    }
  }
  return kept;
}

function leastLateness(jobs: Record<string, [duration: number, deadline: number]>): [string[], number] {
  const order = Object.keys(jobs).sort((a, b) => jobs[a][1] - jobs[b][1]);
  let time = 0;
  let worst = 0;
  for (const name of order) {
    const [duration, deadline] = jobs[name];
    time += duration;
    worst = Math.max(worst, time - deadline);
  }
  return [order, worst];
}

const meetings: Meeting[] = [[1, 3], [2, 5], [4, 7], [6, 8], [5, 9], [8, 10]];
console.log(`meetings kept: ${mostMeetings(meetings).map(([s, e]) => `(${s}, ${e})`).join(" ")}`);
const [order, worst] = leastLateness({ A: [3, 3], B: [1, 6], C: [2, 8], D: [4, 9] });
console.log(`jobs by deadline: ${order.join(" ")}, worst lateness ${worst}`);
OutputRan with node:24.22.0-trixie-slim (Node 24.22.0), TypeScript 5.9.3
meetings kept: (1, 3) (4, 7) (8, 10)
jobs by deadline: A B C D, worst lateness 1

All three printed the same two lines, and a brute-force search over every subset of meetings and every order of jobs printed them too. The highlighted lines hold each algorithm's greedy decision: the sort by end time, the test that a meeting starts no earlier than the last kept one ended, and the sort by deadline. Change the key in either sort and the loop beneath it runs unchanged and gives a different, worse answer.

The meeting test uses >=, so a meeting may start the moment another ends; with > a meeting from 3 to 5 could not follow one from 1 to 3. The six meetings give three either way, because each meeting kept starts an hour after the last one ended, but the activities in a calendar often meet end to end.

Each language sorts with its built-in sort and a key: Python's sorted with key=, Java's List.sort with Comparator.comparingInt, and TypeScript's Array.prototype.sort with a comparison function that subtracts. Python and TypeScript sort a copy, so the caller's list keeps its order; the Java listing copies into a new ArrayList first for the same reason. Each sort is O(n log n), and the scans are O(n).