Card 06/ 08
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
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}")meetings kept: (1, 3) (4, 7) (8, 10)
jobs by deadline: A B C D, worst lateness 1import 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);
}
}meetings kept: (1, 3) (4, 7) (8, 10)
jobs by deadline: A B C D, worst lateness 1type 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}`);meetings kept: (1, 3) (4, 7) (8, 10)
jobs by deadline: A B C D, worst lateness 1All 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).
← → move through sections and cards