Chapter 40: Graph Matchings and Assignment
40.01Intermediate / Advanced4 topics16 min1 interactive animation

Matching basics

A matching pairs up vertices so that no vertex is used twice. Alternating and augmenting paths are the tools that improve a matching, and the difference between a maximal and a maximum matching explains why greedy pairing is not enough.

Learning goals

  • Define a matching and tell matched from free vertices
  • Recognise alternating and augmenting paths
  • Flip an augmenting path and see why the matching grows by one
  • Explain why a maximal matching need not be maximum

Prerequisites

Graph representations and BFS (Chapters 32-33)Bipartite graphs and 2-colouring

Whenever things must be paired off, such as students with projects, jobs with workers or tiles with squares, each thing may be used once. That is a matching. The interesting question is how to pair as many as possible, and the answer is a surprisingly simple local move: find a path that lets you re-pair a few vertices and gain one more pair.

40.01.1

Matched/unmatched vertices

A **matching** in an undirected graph is a set of edges that share no vertex. Every vertex is touched by at most one chosen edge. A vertex that is an end of a chosen edge is **matched** (its other end is its **mate**); every other vertex is **free**, also called unmatched or exposed.

The **size** of a matching is its number of edges, not its number of matched vertices. A matching of size k matches exactly 2k vertices, so a graph with n vertices has a matching of size at most n / 2. A **perfect** matching leaves no vertex free (only possible when n is even); a **near-perfect** matching leaves exactly one.

In this chapter a matching is stored in one of two equivalent ways: as a list of edges {a, b}, or as an array mate[v] holding the partner of v or -1 for a free vertex. The array form makes the questions the algorithms ask, such as “is v free?” and “who is v paired with?”, constant-time.

TermMeaningExample in a graph with edges 0-1, 1-2, 2-3
Matchingedges that share no vertex{0-1, 2-3}
Matched vertextouched by a chosen edge0, 1, 2, 3
Free vertextouched by no chosen edgewith {1-2}: 0 and 3
Sizenumber of chosen edges2 for {0-1, 2-3}
Perfect matchingno free vertex{0-1, 2-3}
Java
1// a matching is a list of edges {a, b}; it is valid when no vertex appears twice
2static boolean isMatching(int n, int[][] matching) {
3 boolean[] used = new boolean[n];
4 for (int[] e : matching) {
5 if (e[0] == e[1] || used[e[0]] || used[e[1]]) return false;
6 used[e[0]] = true;
7 used[e[1]] = true;
8 }
9 return true;
10}
11static List<Integer> freeVertices(int n, int[][] matching) { // the unmatched vertices
12 boolean[] used = new boolean[n];
13 for (int[] e : matching) { used[e[0]] = true; used[e[1]] = true; }
14 List<Integer> free = new ArrayList<>();
15 for (int v = 0; v < n; v++) if (!used[v]) free.add(v);
16 return free;
17}
40.01.2

Alternating path

An **alternating path** is a path whose edges alternate between edges that are **not** in the matching and edges that **are**. Starting from a free vertex, the first edge is a non-matching edge, the second is a matching edge, the third non-matching, and so on.

Think of it as a re-pairing proposal. Moving along a non-matching edge says “let these two be partners”; the next matching edge says “then this vertex loses its old partner”, and that partner needs a new one, which the following non-matching edge supplies. An alternating path is exactly a chain of such swaps.

In a bipartite graph an alternating search from a free left vertex has a rigid shape: it leaves a left vertex along any non-matching edge, arrives at a right vertex, and must leave along that vertex's unique matching edge back to the left side. This is the search the next lessons use.

  • Edge number 1, 3, 5, … (counting from 1) are non-matching edges; edge number 2, 4, 6, … are matching edges.
  • An alternating path may start at a matched vertex too; it is **augmenting** only when both ends are free.
  • An alternating **cycle** returns to its start; flipping it keeps the size but changes which edges are used.
Java
1// mate[v] = partner of v, or -1 when v is free; the path is a vertex sequence
2static boolean isAlternating(int[] path, int[] mate) { // free, matched, free, matched, ...
3 for (int i = 0; i + 1 < path.length; i++) {
4 boolean matched = mate[path[i]] == path[i + 1];
5 if (matched != (i % 2 == 1)) return false; // odd-numbered edges must be matching edges
6 }
7 return true;
8}
40.01.3

Augmenting path

An **augmenting path** is an alternating path that starts at a free vertex and ends at a different free vertex. It therefore has an odd number of edges: k matching edges and k + 1 non-matching edges for some k ≥ 0.

**Flipping** the path swaps the roles of its edges: the k + 1 non-matching edges become matching edges and the k matching edges stop being matching edges. Interior vertices stay matched (they just get a new mate) and the two free end vertices become matched. The matching grows by exactly one edge.

**Berge's theorem** states the whole idea in one line: a matching is maximum if and only if the graph has no augmenting path for it. Every matching algorithm in this chapter is therefore a way of searching for augmenting paths until none is left. Open the animation and load “Greedy gets stuck; a path of length 5 fixes it”: after the greedy step the matching has 2 edges, a single augmenting path of 5 edges is found from vertex 0, and flipping it gives 3.

Java
1static boolean isAugmenting(int[] path, int[] mate) { // mate[v] = partner or -1
2 if (path.length < 2 || path.length % 2 == 1) return false; // an augmenting path has an odd number of edges
3 if (mate[path[0]] != -1 || mate[path[path.length - 1]] != -1) return false; // both ends are free
4 for (int i = 0; i + 1 < path.length; i++)
5 if ((mate[path[i]] == path[i + 1]) != (i % 2 == 1)) return false; // edges alternate: free, matched, free, ...
6 return true;
7}
8static void augment(int[] path, int[] mate) { // flip the path: one more matched edge
9 for (int i = 0; i + 1 < path.length; i += 2) {
10 mate[path[i]] = path[i + 1];
11 mate[path[i + 1]] = path[i];
12 }
13}

Matchings: greedy maximal matching, then augmenting paths

Interactive · edit the input

Your input

Try

left vertices 0, 1, … and right vertices 0′, 1′, …; green edges are matched · 6 vertices · 5 edges

0

free

1

free

2

free

0′

free

1′

free

2′

free

Free vertex being searchedAugmenting pathMatched edge or vertexReached by the alternating search

mate of each left vertex

free0
free1
free2

mate of each right vertex

free0′
free1′
free2′
Setup

A matching is a set of edges that share no vertex. Step 1 scans the edges in the order written and takes every edge whose two ends are both free; step 2 looks for augmenting paths.

Step 1 / 13
Java
1@SuppressWarnings("unchecked")
2static int maxMatching(int left, int right, int[][] edges) { // edges {leftVertex, rightVertex}
3 List<Integer>[] adj = new List[left];
4 for (int u = 0; u < left; u++) adj[u] = new ArrayList<>();
5 for (int[] e : edges) adj[e[0]].add(e[1]);
6 int[] matchL = new int[left], matchR = new int[right];
7 Arrays.fill(matchL, -1);
8 Arrays.fill(matchR, -1);
9 int size = 0;
10 for (int[] e : edges) { // 1. greedy: a maximal matching, edges in input order
11 if (matchL[e[0]] == -1 && matchR[e[1]] == -1) { matchL[e[0]] = e[1]; matchR[e[1]] = e[0]; size++; }
12 }
13 for (int root = 0; root < left; root++) { // 2. try to augment from every free left vertex
14 if (matchL[root] != -1) continue;
15 int[] parent = new int[right]; // parent[v]: left vertex from which v was reached
16 Arrays.fill(parent, -1);
17 Deque<Integer> queue = new ArrayDeque<>(List.of(root));
18 int end = -1;
19 while (!queue.isEmpty() && end == -1) { // alternating BFS
20 int u = queue.poll();
21 for (int v : adj[u]) {
22 if (parent[v] != -1) continue;
23 parent[v] = u; // arrive at v by a non-matching edge
24 if (matchR[v] == -1) { end = v; break; } // free right vertex: augmenting path found
25 queue.add(matchR[v]); // leave v by its matching edge
26 }
27 }
28 if (end == -1) continue; // no augmenting path from root, ever
29 for (int v = end; v != -1; ) { // flip the path
30 int u = parent[v], previous = matchL[u];
31 matchL[u] = v;
32 matchR[v] = u;
33 v = previous;
34 }
35 size++;
36 }
37 return size;
38}

Live state

0 greedy edges0 augmentations

left

3

right

3

edges

5

No condition evaluated on this step
Execution trace (dry run table)Show 13 steps
StepLineStateWhat happens
1L2left=3 right=3 edges=5A matching is a set of edges that share no vertex. Step 1 scans the edges in the order written and takes every edge whose two ends are both free; step 2 looks for augmenting paths.
2L11edge=1-0′ taken=yes size=1Edge 1-0′: both ends are free, so it joins the matching (size 1).
3L11edge=2-1′ taken=yes size=2Edge 2-1′: both ends are free, so it joins the matching (size 2).
4L11edge=0-0′ taken=no size=2Edge 0-0′: right 0′ is already matched, so the edge is skipped.
5L11edge=1-1′ taken=no size=2Edge 1-1′: left 1 is already matched, so the edge is skipped.
6L11edge=2-2′ taken=no size=2Edge 2-2′: left 2 is already matched, so the edge is skipped.
7L13greedy=2 free_left=1Greedy stops with 2 edges. The matching is maximal: no remaining edge has two free ends. That does not make it maximum, so augmenting paths are searched from each free left vertex.
8L15root=0Alternating search from the free left vertex 0: leave a left vertex by any non-matching edge, leave a right vertex by its matching edge.
9L25u=0 v=0′ next=10′ is matched to 1, so the search continues from 1.
10L25u=1 v=1′ next=21′ is matched to 2, so the search continues from 2.
11L24u=2 v=2′ end=2′Right vertex 2′ is free: an augmenting path from 0 to 2′ exists (it alternates free, matched, free edges).
12L35size=3 augmentations=1Flipping the path adds one edge: the matching now has 3. Every vertex that was matched stays matched.
13L37return=3 greedy=2 augmentations=1Maximum matching size 3. Greedy had 2; 1 augmentation raised it to 3. No augmenting path remains, which is exactly the condition for a maximum matching.
40.01.4

Maximum vs maximal matching

A matching is **maximal** when no further edge can be added without breaking the matching: every edge has at least one matched end. A matching is **maximum** when no other matching is larger. Maximum implies maximal, but not the other way round.

The greedy rule “scan the edges and take every edge whose ends are both free” always produces a maximal matching, and it is fast, but it can be unlucky. With edges 0-0′, 0-1′, 1-0′ (left vertices 0, 1, right vertices 0′, 1′) greedy takes 0-0′ first and is then stuck at size 1, although 0-1′ and 1-0′ form a matching of size 2. Run the animation with “Greedy is stuck at 1; one swap reaches 2” to see the augmenting path 1 → 0′ → 0 → 1′ repair it.

The damage greedy can do is bounded: every maximal matching has at least **half** the size of a maximum matching, because each edge of a maximum matching shares a vertex with some edge of a maximal one, and each edge of the maximal matching can block at most two of them. So greedy is a 2-approximation, which is fine for rough answers but not for exact ones.

KindConditionFound by
Maximalno edge can be addedone greedy scan, O(E)
Maximumno larger matching existsaugmenting paths until none remain
Perfectevery vertex matcheda maximum matching that happens to use all vertices
Java
1static int[][] greedyMaximal(int n, int[][] edges) { // takes edges in the given order
2 boolean[] used = new boolean[n];
3 List<int[]> matching = new ArrayList<>();
4 for (int[] e : edges) {
5 if (e[0] != e[1] && !used[e[0]] && !used[e[1]]) {
6 used[e[0]] = true;
7 used[e[1]] = true;
8 matching.add(e);
9 }
10 }
11 return matching.toArray(new int[0][]);
12}
13static boolean isMaximal(int n, int[][] edges, int[][] matching) { // no edge could still be added
14 boolean[] used = new boolean[n];
15 for (int[] e : matching) { used[e[0]] = true; used[e[1]] = true; }
16 for (int[] e : edges) if (e[0] != e[1] && !used[e[0]] && !used[e[1]]) return false;
17 return true;
18}

Worst

greedy O(V + E); a maximum matching needs the algorithms of the next lessons

Space

O(V)

Practice ladder

Now try it yourself

Easy

  • Assign Cookies

Medium

  • Maximum Number of Accepted Invitations

Advanced

  • Maximum Students Taking Exam

Key takeaways

  • A matching uses each vertex at most once; its size counts edges.
  • An augmenting path alternates free/matched edges between two free vertices, and flipping it adds one edge.
  • A matching is maximum exactly when no augmenting path exists (Berge); a maximal matching is only a lower-bound guess.