appointment scheduling
algorithm design
constraint satisfaction
optimization
computational complexity

Appointment scheduling algorithm N people with N free-busy slots, constraint-satisfaction

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Scheduling one appointment for each of N people across available time slots is a classic constraint problem. The right algorithm depends on what the constraints actually are: if each person needs one slot and each slot can host one person, the problem becomes bipartite matching; if you add preferences, room capacities, travel gaps, or soft constraints, the model moves closer to a full constraint-satisfaction or optimization problem.

Model the Basic Version as Bipartite Matching

The cleanest starting point is a bipartite graph:

  • left side: people
  • right side: slots
  • edge: a person is available for that slot

A feasible schedule is then a matching that covers every person. If such a matching exists, everyone gets a slot with no conflicts. This is much more precise than trying arbitrary greedy choices.

For small and medium problems, a DFS-based augmenting-path matcher is easy to implement and works well.

python
1def can_assign(person, graph, seen, match_to_person):
2    for slot in graph[person]:
3        if slot in seen:
4            continue
5        seen.add(slot)
6
7        if slot not in match_to_person or can_assign(match_to_person[slot], graph, seen, match_to_person):
8            match_to_person[slot] = person
9            return True
10    return False
11
12
13def schedule(graph, people):
14    match_to_person = {}
15
16    for person in people:
17        if not can_assign(person, graph, set(), match_to_person):
18            return None
19
20    return {person: slot for slot, person in match_to_person.items()}
21
22
23people = ["A", "B", "C"]
24graph = {
25    "A": ["09:00", "10:00"],
26    "B": ["10:00", "11:00"],
27    "C": ["09:00", "11:00"],
28}
29
30print(schedule(graph, people))

This returns a valid one-person-per-slot assignment when one exists.

Why Greedy Scheduling Often Fails

A common first attempt is to assign each person their earliest free slot. That can fail even when a valid full schedule exists. The reason is that early local choices can block a later person who has fewer alternatives.

Matching algorithms avoid that by allowing reassignment through augmenting paths. If person B needs slot 10:00, but it is currently assigned to A, the algorithm checks whether A can move to another acceptable slot. That is exactly the sort of backtracking a naive greedy algorithm misses.

When It Becomes a CSP Instead of Plain Matching

The problem stops being simple matching once you add richer rules such as:

  • certain people must avoid adjacent slots,
  • some meetings need longer durations,
  • one slot can host multiple people but only up to a capacity,
  • preferences should be optimized, not just satisfied,
  • some assignments are allowed but expensive.

At that point, a constraint solver or integer-programming model is often a better fit than hand-written matching code. Libraries such as OR-Tools are useful because they let you state the constraints directly and then optimize for fairness, preference score, or total waiting time.

Backtracking Still Works for Small CSPs

For small N, plain backtracking with good pruning can still be fine. The important part is to order variables intelligently, usually by scheduling the most constrained person first.

python
1def backtrack(assignments, remaining_people, domains):
2    if not remaining_people:
3        return assignments
4
5    person = min(remaining_people, key=lambda p: len(domains[p]))
6
7    for slot in domains[person]:
8        if slot in assignments.values():
9            continue
10
11        assignments[person] = slot
12        result = backtrack(assignments, [p for p in remaining_people if p != person], domains)
13        if result is not None:
14            return result
15        del assignments[person]
16
17    return None

This is conceptually simple, but worst-case growth is still exponential, so it is best used when the search space is naturally small.

Practical Design Rule

If the requirement is only one person per slot with availability constraints, use bipartite matching. If the requirement includes optimization or richer business rules, use a CSP or optimization solver. That distinction saves a lot of unnecessary complexity.

Common Pitfalls

  • Using a greedy earliest-slot rule and assuming it will always find a valid schedule.
  • Treating a matching problem like a generic CSP when the simpler graph model is enough.
  • Ignoring whether the real goal is feasibility or optimization.
  • Failing to schedule the most constrained people first in backtracking solutions.
  • Encoding preferences without deciding whether they are hard constraints or soft goals.

Summary

  • Basic appointment scheduling maps naturally to bipartite matching.
  • Matching algorithms outperform naive greedy assignment on constrained inputs.
  • Richer rules turn the problem into a full CSP or optimization problem.
  • Backtracking is acceptable for small instances when paired with good pruning.
  • Choosing the right model matters more than memorizing one scheduling algorithm.

Course illustration
Course illustration

All Rights Reserved.