Jump to content

Blossom algorithm

From Rosetta Code
Task
Blossom algorithm
You are encouraged to solve this task according to the task description, using any language you may know.


The Blossom Algorithm
The Blossom algorithm, developed by Jack Edmonds in 1965, is a seminal algorithm that solves the maximum matching problem for general graphs by extending the augmenting path concept to handle odd cycles.
Core Idea
Shrinking Blossoms
The key insight of the Blossom algorithm is to identify an odd cycle (a blossom) when encountered during the search for an augmenting path. When a blossom is detected, the algorithm contracts or shrinks the entire cycle into a single "pseudo-vertex". This creates a smaller, auxiliary graph.
The search for an augmenting path then continues in this contracted graph. If an augmenting path is found in the contracted graph that involves a pseudo-vertex (representing a shrunk blossom), this path can be "lifted" back to the original graph. The structure of the blossom allows the algorithm to find a corresponding augmenting path in the original graph that utilizes edges within the blossom.
This process of shrinking blossoms, searching for augmenting paths in the contracted graph, and expanding blossoms to find paths in the original graph is repeated until no more augmenting paths are found, at which point the current matching is maximum.


Problem
Maximum Matching in General Graphs
The problem addressed by the Blossom algorithm is finding a Maximum matching|maximum matching in a Graph (discrete mathematics)|general graph. Unlike Bipartite graph|bipartite graphs, general graphs can contain Cycle (graph theory)|odd-length cycles.
Graph and Matching Definition
A Graph (discrete mathematics)|graph G is formally defined by a set of Vertex (graph theory)|vertices V and a set of Edge (graph theory)|edges E, denoted as . An edge connects two vertices.
A Matching (graph theory)|matching M in G is a subset of the edges E such that no two edges in M share a common vertex. That is, for any two distinct edges and , the sets of vertices and are disjoint.
Maximum Matching
A Maximum matching|maximum matching is a matching M with the largest possible number of edges among all possible matchings in the graph G. The size of the matching is .
The objective of the maximum matching problem is to find a matching M such that is maximized.
Challenge in General Graphs
Odd Cycles
For Bipartite graph|bipartite graphs, efficient algorithms exist (e.g., Hopcroft-Karp algorithm) that rely on finding Augmenting path|augmenting paths. An augmenting path with respect to a matching M is a path that starts and ends at unmatched vertex|unmatched vertices, and its edges alternate between being outside M and inside M. If such a path exists, the matching can be increased by one edge by flipping the edges along the path (taking edges not in M and removing edges in M along the path). Berge's lemma states that a matching is maximum if and only if it has no augmenting path.
However, the presence of Cycle (graph theory)|odd-length cycles in general graphs complicates the search for augmenting paths. Standard Breadth-first search|BFS techniques used for bipartite graphs can get "confused" by odd cycles, which are sometimes called blossoms or flowers in this context. An odd cycle can contain an even number of matched edges and an odd number of unmatched edges with respect to an alternating path leading into it, potentially preventing the discovery of a true augmenting path using simple methods.


Objective
Given a general graph , the problem is to compute a matching such that its size is maximum. The Blossom algorithm provides a polynomial-time solution to this problem.
Complexity
The original Blossom algorithm by Edmonds has a time complexity of . Subsequent improvements by Edmonds and others, including Robert Tarjan|Tarjan, led to more efficient versions. Modern implementations using advanced data structures achieve a time complexity of , which is generally considered very efficient for this problem. An variant is also commonly cited and easier to implement.


C++

Works with: C++17
Translation of: Python
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

struct Blossom {
    int n;
    const vector<vector<int>>& adj;
    vector<int> match, p, base;
    vector<bool> used, blossom;
    queue<int> q;

    Blossom(const vector<vector<int>>& _adj)
        : n((int)_adj.size()), adj(_adj),
          match(n, -1), p(n), base(n),
          used(n), blossom(n)
    {}

    int lca(int a, int b) {
        vector<bool> used_path(n, false);
        while (true) {
            a = base[a];
            used_path[a] = true;
            if (match[a] < 0) break;
            a = p[match[a]];
        }
        while (true) {
            b = base[b];
            if (used_path[b]) return b;
            b = p[match[b]];
        }
    }

    void markPath(int v, int b, int x) {
        // mark blossom vertices on path from v to base b
        while (base[v] != b) {
            blossom[base[v]] = blossom[base[match[v]]] = true;
            p[v] = x;
            x = match[v];
            v = p[x];
        }
    }

    bool findPath(int root) {
        fill(used.begin(), used.end(), false);
        fill(p.begin(), p.end(), -1);
        for (int i = 0; i < n; ++i) base[i] = i;
        while (!q.empty()) q.pop();

        used[root] = true;
        q.push(root);

        while (!q.empty()) {
            int v = q.front(); q.pop();
            for (int u : adj[v]) {
                if (base[v] == base[u] || match[v] == u)
                    continue;
                if (u == root || (match[u] >= 0 && p[match[u]] >= 0)) {
                    // blossom found
                    int curbase = lca(v, u);
                    fill(blossom.begin(), blossom.end(), false);
                    markPath(v, curbase, u);
                    markPath(u, curbase, v);
                    for (int i = 0; i < n; ++i) {
                        if (blossom[base[i]]) {
                            base[i] = curbase;
                            if (!used[i]) {
                                used[i] = true;
                                q.push(i);
                            }
                        }
                    }
                }
                else if (p[u] < 0) {
                    // extend tree
                    p[u] = v;
                    if (match[u] < 0) {
                        // augmenting path found
                        int cur = u;
                        while (cur != -1) {
                            int prev = p[cur];
                            int nxt = (prev >= 0 ? match[prev] : -1);
                            match[cur] = prev;
                            match[prev] = cur;
                            cur = nxt;
                        }
                        return true;
                    }
                    used[match[u]] = true;
                    q.push(match[u]);
                }
            }
        }
        return false;
    }

    // Returns {match, size_of_matching}
    pair<vector<int>,int> solve() {
        int res = 0;
        for (int v = 0; v < n; ++v) {
            if (match[v] < 0 && findPath(v))
                ++res;
        }
        return { match, res };
    }
};

int main() {
    // Example: 5‑cycle (odd cycle) 0–1–2–3–4–0
    int n = 5;
    vector<pair<int,int>> edges = {{0,1},{1,2},{2,3},{3,4},{4,0}};
    vector<vector<int>> adj(n);
    for (auto& e : edges) {
        adj[e.first].push_back(e.second);
        adj[e.second].push_back(e.first);
    }

    Blossom bf(adj);
    auto [match, msize] = bf.solve();

    cout << "Maximum matching size: " << msize << "\n";
    cout << "Matched pairs:\n";
    for (int u = 0; u < n; ++u) {
        int v = match[u];
        if (v >= 0 && u < v) {
            cout << "  " << u << " – " << v << "\n";
        }
    }
    return 0;
}
Output:
Maximum matching size: 2
Matched pairs:
  0 – 1
  2 – 3

FreeBASIC

Type Queue
    Dato(Any) As Integer
    Front As Integer
    Rear As Integer
    Size As Integer
    Capacity As Integer
End Type

' Queue operations
Function CreateQueue(capacity As Integer) As Queue
    Dim q As Queue
    q.Capacity = capacity
    q.Size = 0
    q.Front = 0
    q.Rear = -1
    Redim q.Dato(capacity - 1)
    Return q
End Function

Function isEmpty(q As Queue) As Integer
    Return (q.Size = 0)
End Function

Sub Enqueue(Byref q As Queue, item As Integer)
    q.Rear = (q.Rear + 1) Mod q.Capacity
    q.Dato(q.Rear) = item
    q.Size += 1
End Sub

Function Dequeue(Byref q As Queue) As Integer
    Dim item As Integer = q.Dato(q.Front)
    q.Front = (q.Front + 1) Mod q.Capacity
    q.Size -= 1
    Return item
End Function

Sub ClearQueue(Byref q As Queue)
    q.Size = 0
    q.Front = 0
    q.Rear = -1
End Sub

' Global variables for the algorithm
Dim Shared g_n As Integer
Dim Shared g_adj() As Integer
Dim Shared g_adjSize() As Integer
Dim Shared g_match() As Integer
Dim Shared g_p() As Integer
Dim Shared g_base() As Integer
Dim Shared g_used() As Integer
Dim Shared g_blossom() As Integer
Dim Shared g_q As Queue

' Find least common ancestor of a and b in the forest of alternating tree
Function LCA(a As Integer, b As Integer) As Integer
    Dim usedPath(g_n-1) As Integer
    
    ' Mark path from a to root
    Do
        a = g_base(a)
        usedPath(a) = 1
        If g_match(a) = -1 Then Exit Do
        a = g_p(g_match(a))
    Loop
    
    ' Find first marked vertex in path from b to root
    Do
        b = g_base(b)
        If usedPath(b) Then Return b
        b = g_p(g_match(b))
    Loop
    
    Return -1 ' Should never reach here
End Function

' Mark vertices along the path from v to base b, setting their parent to x
Sub MarkPath(v As Integer, b As Integer, x As Integer)
    While g_base(v) <> b
        g_blossom(g_base(v)) = 1
        g_blossom(g_base(g_match(v))) = 1
        g_p(v) = x
        x = g_match(v)
        v = g_p(x)
    Wend
End Sub

' Try to find an augmenting path starting from root
Function FindPath(root As Integer) As Boolean
    Dim As Integer i, j
    ' Reset arrays
    For i = 0 To g_n-1
        g_used(i) = 0
        g_p(i) = -1
        g_base(i) = i
        g_blossom(i) = 0
    Next
    
    ClearQueue(g_q)
    g_used(root) = 1
    Enqueue(g_q, root)
    
    While Not isEmpty(g_q)
        Dim v As Integer = Dequeue(g_q)
        
        For i = 0 To g_adjSize(v) - 1
            Dim u As Integer = g_adj(v * g_n + i)
            
            ' Two cases to skip
            If g_base(v) = g_base(u) Or g_match(v) = u Then Continue For
            
            ' Found a blossom or an odd cycle edge
            If u = root Or (g_match(u) <> -1 And g_p(g_match(u)) <> -1) Then
                Dim curbase As Integer = LCA(v, u)
                
                ' Reset blossom array
                For j = 0 To g_n-1
                    g_blossom(j) = 0
                Next
                
                MarkPath(v, curbase, u)
                MarkPath(u, curbase, v)
                
                ' Contract blossom
                For j = 0 To g_n-1
                    If g_blossom(g_base(j)) Then
                        g_base(j) = curbase
                        If Not g_used(j) Then
                            g_used(j) = 1
                            Enqueue(g_q, j)
                        End If
                    End If
                Next
                
                ' Otherwise extend the alternating tree
            Elseif g_p(u) = -1 Then
                g_p(u) = v
                
                If g_match(u) = -1 Then
                    ' Augmenting path found: flip matches along the path
                    Dim curr As Integer = u
                    While curr <> -1
                        Dim prev As Integer = g_p(curr)
                        If prev = -1 Then Exit While
                        Dim sgte As Integer = g_match(prev)
                        g_match(curr) = prev
                        g_match(prev) = curr
                        curr = sgte
                    Wend
                    Return True
                End If
                
                ' Else continue BFS from the matched partner
                g_used(g_match(u)) = 1
                Enqueue(g_q, g_match(u))
            End If
        Next
    Wend
    
    Return False
End Function

' Edmonds' Blossom Algorithm
Sub MaxMatching(adj() As Integer, adjSize() As Integer, n As Integer, match() As Integer, Byref matchSize As Integer)
    Dim As Integer i, j, v
    ' Set up global variables
    g_n = n
    Redim g_adj(n*n-1)
    Redim g_adjSize(n-1)
    Redim g_match(n-1)
    Redim g_p(n-1)
    Redim g_base(n-1)
    Redim g_used(n-1)
    Redim g_blossom(n-1)
    g_q = CreateQueue(n * n)
    
    ' Copy input arrays to global arrays
    For i = 0 To n-1
        g_adjSize(i) = adjSize(i)
        For j = 0 To adjSize(i)-1
            g_adj(i * n + j) = adj(i * n + j)
        Next
    Next
    
    ' Initialize match array
    For i = 0 To n-1
        g_match(i) = -1
    Next
    
    ' Main loop: try to grow matching by finding augmenting paths
    matchSize = 0
    For v = 0 To n-1
        If g_match(v) = -1 Then
            If FindPath(v) Then matchSize += 1
        End If
    Next
    
    ' Copy result back to output array
    For i= 0 To n-1
        match(i) = g_match(i)
    Next    
End Sub

' Example: 5-cycle (odd cycle)
' Vertices: 0-1-2-3-4-0
Dim As Integer n = 5
Dim As Integer edges(4, 1) = { {0,1}, {1,2}, {2,3}, {3,4}, {4,0} }

Dim As Integer i, u, v
' Create adjacency list representation
Dim As Integer adj(n*n-1)    ' Flattened adjacency list
Dim As Integer adjSize(n-1)  ' Size of each adjacency list

For i = 0 To 4
    u = edges(i, 0)
    v = edges(i, 1)
    
    adj(u * n + adjSize(u)) = v
    adjSize(u) += 1
    
    adj(v * n + adjSize(v)) = u
    adjSize(v) += 1
Next

' Find maximum matching
Dim As Integer match(n-1)
Dim As Integer matchSize

MaxMatching(adj(), adjSize(), n, match(), matchSize)

' Print results
Print "Maximum matching size:"; matchSize
Print "Matched pairs:"

Dim seen(n*n-1) As Integer
For u = 0 To n-1
    v = match(u)
    If v <> -1 And u < v Then Print "  " & u & " - " & v
Next

Sleep
Output:
Maximum matching size: 2
Matched pairs:
  0 – 1
  2 – 3.

Go

Translation of: C++
package main

import (
    "fmt"
)

type Blossom struct {
    n        int
    adj      [][]int
    match    []int
    p        []int
    base     []int
    used     []bool
    blossom  []bool
    queue    []int
}

func NewBlossom(adj [][]int) *Blossom {
    n := len(adj)
    match := make([]int, n)
    p := make([]int, n)
    base := make([]int, n)
    used := make([]bool, n)
    blossom := make([]bool, n)
    for i := 0; i < n; i++ {
        match[i] = -1
        p[i] = -1
        base[i] = i
    }
    return &Blossom{
        n:       n,
        adj:     adj,
        match:   match,
        p:       p,
        base:    base,
        used:    used,
        blossom: blossom,
        queue:   make([]int, 0, n),
    }
}

// least common ancestor of x and y in the alternating forest
func (bm *Blossom) lca(x, y int) int {
    usedPath := make([]bool, bm.n)
    for {
        x = bm.base[x]
        usedPath[x] = true
        if bm.match[x] < 0 {
            break
        }
        x = bm.p[bm.match[x]]
    }
    for {
        y = bm.base[y]
        if usedPath[y] {
            return y
        }
        y = bm.p[bm.match[y]]
    }
}

// mark path from v up to base0, setting parents to x
func (bm *Blossom) markPath(v, base0, x int) {
    for bm.base[v] != base0 {
        mv := bm.match[v]
        bm.blossom[bm.base[v]] = true
        bm.blossom[bm.base[mv]] = true
        bm.p[v] = x
        x = mv
        v = bm.p[x]
    }
}

// attempt to find an augmenting path from root
func (bm *Blossom) findPath(root int) bool {
    // reset BFS state
    for i := 0; i < bm.n; i++ {
        bm.used[i] = false
        bm.p[i] = -1
        bm.base[i] = i
    }
    bm.queue = bm.queue[:0]

    bm.used[root] = true
    bm.queue = append(bm.queue, root)

    qi := 0
    for qi < len(bm.queue) {
        v := bm.queue[qi]
        qi++
        for _, u := range bm.adj[v] {
            if bm.base[v] == bm.base[u] || bm.match[v] == u {
                continue
            }
            // blossom found
            if u == root || (bm.match[u] >= 0 && bm.p[bm.match[u]] >= 0) {
                curbase := bm.lca(v, u)
                for i := 0; i < bm.n; i++ {
                    bm.blossom[i] = false
                }
                bm.markPath(v, curbase, u)
                bm.markPath(u, curbase, v)
                for i := 0; i < bm.n; i++ {
                    if bm.blossom[bm.base[i]] {
                        bm.base[i] = curbase
                        if !bm.used[i] {
                            bm.used[i] = true
                            bm.queue = append(bm.queue, i)
                        }
                    }
                }
            } else if bm.p[u] < 0 {
                // extend tree
                bm.p[u] = v
                if bm.match[u] < 0 {
                    // augmenting path found
                    cur := u
                    for cur >= 0 {
                        prev := bm.p[cur]
                        nxt := -1
                        if prev >= 0 {
                            nxt = bm.match[prev]
                            bm.match[cur] = prev
                            bm.match[prev] = cur
                        }
                        cur = nxt
                    }
                    return true
                }
                // enqueue matched partner
                mu := bm.match[u]
                if !bm.used[mu] {
                    bm.used[mu] = true
                    bm.queue = append(bm.queue, mu)
                }
            }
        }
    }
    return false
}

// Solve returns the matching array and the size of the matching
func (bm *Blossom) Solve() ([]int, int) {
    res := 0
    for v := 0; v < bm.n; v++ {
        if bm.match[v] < 0 {
            if bm.findPath(v) {
                res++
            }
        }
    }
    return bm.match, res
}

func main() {
    // Example: 5‑cycle 0–1–2–3–4–0
    n := 5
    edges := [][2]int{{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0}}
    adj := make([][]int, n)
    for _, e := range edges {
        u, v := e[0], e[1]
        adj[u] = append(adj[u], v)
        adj[v] = append(adj[v], u)
    }

    bm := NewBlossom(adj)
    match, size := bm.Solve()

    fmt.Printf("Maximum matching size: %d\n", size)
    fmt.Println("Matched pairs:")
    for u, v := range match {
        if v >= 0 && u < v {
            fmt.Printf("  %d – %d\n", u, v)
        }
    }
}
Output:
Maximum matching size: 2
Matched pairs:
  0 – 1
  2 – 3


Java

Translation of: C++
import java.util.*;

public class Main {
    private int n;
    private List<List<Integer>> adj;
    private int[] match, p, base;
    private boolean[] used, blossom;
    private Deque<Integer> queue;

    public Main(List<List<Integer>> adj) {
        this.n = adj.size();
        this.adj = adj;
        this.match = new int[n];
        this.p = new int[n];
        this.base = new int[n];
        this.used = new boolean[n];
        this.blossom = new boolean[n];
        this.queue = new ArrayDeque<>();
        Arrays.fill(match, -1);
    }

    // Find least common ancestor of a and b in the alternating forest
    private int lca(int a, int b) {
        boolean[] usedPath = new boolean[n];
        while (true) {
            a = base[a];
            usedPath[a] = true;
            if (match[a] < 0) break;
            a = p[match[a]];
        }
        while (true) {
            b = base[b];
            if (usedPath[b]) return b;
            b = p[match[b]];
        }
    }

    // Mark vertices along the path from v to base b, setting their parent to x
    private void markPath(int v, int b, int x) {
        while (base[v] != b) {
            int mv = match[v];
            blossom[base[v]] = true;
            blossom[base[mv]] = true;
            p[v] = x;
            x = mv;
            v = p[x];
        }
    }

    // Try to find an augmenting path starting from root
    private boolean findPath(int root) {
        Arrays.fill(used, false);
        Arrays.fill(p, -1);
        for (int i = 0; i < n; i++) {
            base[i] = i;
        }
        queue.clear();

        used[root] = true;
        queue.add(root);

        while (!queue.isEmpty()) {
            int v = queue.poll();
            for (int u : adj.get(v)) {
                if (base[v] == base[u] || match[v] == u) {
                    continue;
                }
                // Blossom found
                if (u == root || (match[u] >= 0 && p[match[u]] >= 0)) {
                    int curbase = lca(v, u);
                    Arrays.fill(blossom, false);
                    markPath(v, curbase, u);
                    markPath(u, curbase, v);
                    for (int i = 0; i < n; i++) {
                        if (blossom[base[i]]) {
                            base[i] = curbase;
                            if (!used[i]) {
                                used[i] = true;
                                queue.add(i);
                            }
                        }
                    }
                } else if (p[u] < 0) {
                    // Extend the alternating tree
                    p[u] = v;
                    // Found augmenting path
                    if (match[u] < 0) {
                        int cur = u;
                        while (cur >= 0) {
                            int prev = p[cur];
                            int next = (prev >= 0 ? match[prev] : -1);
                            match[cur] = prev;
                            match[prev] = cur;
                            cur = next;
                        }
                        return true;
                    }
                    // Continue BFS from the matched partner
                    int mu = match[u];
                    if (!used[mu]) {
                        used[mu] = true;
                        queue.add(mu);
                    }
                }
            }
        }
        return false;
    }

    // Compute maximum matching; returns the size
    public int solve() {
        int res = 0;
        for (int v = 0; v < n; v++) {
            if (match[v] < 0) {
                if (findPath(v)) {
                    res++;
                }
            }
        }
        return res;
    }

    public int[] getMatch() {
        return match;
    }

    public static void main(String[] args) {
        // Example: 5‑cycle (odd cycle) 0–1–2–3–4–0
        int n = 5;
        int[][] edges = { {0,1}, {1,2}, {2,3}, {3,4}, {4,0} };
        List<List<Integer>> adj = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            adj.add(new ArrayList<>());
        }
        for (int[] e : edges) {
            adj.get(e[0]).add(e[1]);
            adj.get(e[1]).add(e[0]);
        }

        Main blossom = new Main(adj);
        int msize = blossom.solve();
        int[] match = blossom.getMatch();

        System.out.println("Maximum matching size: " + msize);
        System.out.println("Matched pairs:");
        for (int u = 0; u < n; u++) {
            int v = match[u];
            if (v >= 0 && u < v) {
                System.out.println("  " + u + " – " + v);
            }
        }
    }
}
Output:
Maximum matching size: 2
Matched pairs:
  0 – 1
  2 – 3


JavaScript

Translation of: C++
class Blossom {
  constructor(adj) {
    this.n = adj.length;
    this.adj = adj;
    this.match = new Array(this.n).fill(-1);
    this.p = new Array(this.n).fill(-1);
    this.base = new Array(this.n);
    this.used = new Array(this.n).fill(false);
    this.blossom = new Array(this.n).fill(false);
    this.queue = [];
  }

  // Find least common ancestor of a and b in the alternating forest
  lca(a, b) {
    const usedPath = new Array(this.n).fill(false);
    while (true) {
      a = this.base[a];
      usedPath[a] = true;
      if (this.match[a] === -1) break;
      a = this.p[this.match[a]];
    }
    while (true) {
      b = this.base[b];
      if (usedPath[b]) return b;
      b = this.p[this.match[b]];
    }
  }

  // Mark path from v up to base0, setting parents to x
  markPath(v, base0, x) {
    while (this.base[v] !== base0) {
      const mv = this.match[v];
      this.blossom[this.base[v]] = this.blossom[this.base[mv]] = true;
      this.p[v] = x;
      x = mv;
      v = this.p[x];
    }
  }

  // Try to find an augmenting path from root
  findPath(root) {
    this.used.fill(false);
    this.p.fill(-1);
    for (let i = 0; i < this.n; i++) {
      this.base[i] = i;
    }
    this.queue = [];

    this.used[root] = true;
    this.queue.push(root);

    while (this.queue.length > 0) {
      const v = this.queue.shift();
      for (const u of this.adj[v]) {
        // Skip same base or matched edge
        if (this.base[v] === this.base[u] || this.match[v] === u) {
          continue;
        }
        // Blossom detected
        if (
          u === root ||
          (this.match[u] !== -1 && this.p[this.match[u]] !== -1)
        ) {
          const curbase = this.lca(v, u);
          this.blossom.fill(false);
          this.markPath(v, curbase, u);
          this.markPath(u, curbase, v);
          for (let i = 0; i < this.n; i++) {
            if (this.blossom[this.base[i]]) {
              this.base[i] = curbase;
              if (!this.used[i]) {
                this.used[i] = true;
                this.queue.push(i);
              }
            }
          }
        }
        // Otherwise, extend the alternating tree
        else if (this.p[u] === -1) {
          this.p[u] = v;
          // If u is free, we found an augmenting path
          if (this.match[u] === -1) {
            let cur = u;
            while (cur !== -1) {
              const prev = this.p[cur];
              const next = prev === -1 ? -1 : this.match[prev];
              this.match[cur] = prev;
              this.match[prev] = cur;
              cur = next;
            }
            return true;
          }
          // Enqueue the matched partner
          const mu = this.match[u];
          if (!this.used[mu]) {
            this.used[mu] = true;
            this.queue.push(mu);
          }
        }
      }
    }
    return false;
  }

  // Compute maximum matching; returns [matchArray, size]
  solve() {
    let size = 0;
    for (let v = 0; v < this.n; v++) {
      if (this.match[v] === -1) {
        if (this.findPath(v)) {
          size++;
        }
      }
    }
    return [this.match, size];
  }
}

// Example usage
(function() {
  // 5-cycle: 0–1–2–3–4–0
  const n = 5;
  const edges = [
    [0, 1],
    [1, 2],
    [2, 3],
    [3, 4],
    [4, 0],
  ];
  const adj = Array.from({ length: n }, () => []);
  for (const [u, v] of edges) {
    adj[u].push(v);
    adj[v].push(u);
  }

  const blossom = new Blossom(adj);
  const [match, msize] = blossom.solve();

  console.log(`Maximum matching size: ${msize}`);
  console.log("Matched pairs:");
  const seen = new Set();
  for (let u = 0; u < n; u++) {
    const v = match[u];
    if (v !== -1 && !seen.has(`${v},${u}`)) {
      console.log(`  ${u}${v}`);
      seen.add(`${u},${v}`);
    }
  }
})();
Output:
Maximum matching size: 2
Matched pairs:
  0 – 1
  2 – 3

Julia

Translation of: Python
using DataStructures # for Deque

struct BlossomMatching
	nv::Int # number of vertices
	matches::Vector{Int}
	parents::Vector{Int}
	base_vertices::Vector{Int}
	used::Vector{Bool}
	blossom::Vector{Bool}
end

"""Find least common ancestor of a and b in the forest of alternating tree."""
function lca(m::BlossomMatching, a, b)
	used_path = falses(m.nv)
	while true
		a = m.base_vertices[a]
		used_path[a] = true
		m.matches[a] == 0 && break
		a = m.parents[m.matches[a]]
	end
	while true
		b = m.base_vertices[b]
		used_path[b] && return b
		b = m.parents[m.matches[b]]
	end
end

"""Mark vertices along the path from v to base b, setting their parent to x."""
function mark_path!(m::BlossomMatching, v, b, x)
	while m.base_vertices[v] != b
		m.blossom[m.base_vertices[v]] = true
		m.blossom[m.base_vertices[m.matches[v]]] = true
		m.parents[v] = x
		x = m.matches[v]
		v = m.parents[x]
	end
end

"""Try to find an augmenting path starting from root."""
function find_path!(m::BlossomMatching, adj, root)
	# reset struct
	m.used .= false
	m.parents .= 0
	m.base_vertices .= collect(1:m.nv)
	q = Deque{Int}()
	m.used[root] = true
	push!(q, root)
	while !isempty(q)
		v = popfirst!(q)
		for u in adj[v]
			# two cases to skip
			if m.base_vertices[v] == m.base_vertices[u] || m.matches[v] == u
				continue
			end
			# found a blossom or an odd cycle edge
			if u == root || m.matches[u] != 0 && m.parents[m.matches[u]] != 0
				curbase = lca(m, v, u)
				m.blossom .= false
				mark_path!(m, v, curbase, u)
				mark_path!(m, u, curbase, v)
				# contract blossom
				for i in 1:m.nv
					if m.blossom[m.base_vertices[i]]
						m.base_vertices[i] = curbase
						if !m.used[i]
							m.used[i] = true
							push!(q, i)
						end
					end
				end
				# otherwise extend the alternating tree
			elseif m.parents[u] == 0
				m.parents[u] = v
				if m.matches[u] == 0
					# augmenting path found: flip matches along the path
					curr = u
					while curr != 0
						prev = m.parents[curr]
						nxt = prev != 0 ? m.matches[prev] : 0
						m.matches[curr] = prev
						m.matches[prev] = curr
						curr = nxt
					end
					return true
				end
				# else continue BFS from the matches partner
				m.used[m.matches[u]] = true
				push!(q, m.matches[u])
			end
		end
	end
	return false
end

"""
	Finds maximum matching in a general undirected graph using Edmonds' Blossom algorithm.
	Input: adj — list of lists, adj[u] is the list of neighbors of u (1-indexed).
	Returns: match, size
	  match — list where match[u] = v if u–v is in the matching, or 0
	  size  — number of matches edges
"""
function max_matching(n, adj)
	n <= 0 && return Int[], 0 # since the graph has no edges
	m = BlossomMatching(n, zeros(Int, n), zeros(Int, n), zeros(Int, n), falses(n), falses(n))
	#  grow matching by finding augmenting paths
	match_count = count(v -> m.matches[v] == 0 && find_path!(m, adj, v), 1:n)
	return m.matches, match_count
end

function test_blossom()
	# Example: 5‑cycle (odd cycle)
	# Vertices: 0–1–2–3–4–0
	nv = 5
	edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)]
	adj = [Int[] for _ in 1:nv]
	for (u, v) in edges
		push!(adj[u + 1], v + 1) # convert zero based to one based input
	end
	matches, msize = max_matching(nv, adj)
	println("Maximum matching size: $msize")
	println("Matched pairs:")
	seen = Set{Tuple{Int, Int}}()
	for (u, v) in enumerate(matches)
		if v != 0 && (v, u)  seen
			println("  $(u - 1)$(v - 1)")  # 1 based to 0 based output
			push!(seen, (u, v))
		end
	end
end

test_blossom()
Output:
Maximum matching size: 2
Matched pairs:
  0 – 1
  2 – 3

Phix

Translation of: Go –  except using more idiomatic sequences, local if it can, rather than a struct of sequences, and 1-based indices
integer n
sequence matches, paths, bases, blossom

// least common ancestor of x and y in the alternating forest
function lca(integer x, y)
    sequence usedPath := repeat(false, n)
    while true do
        x = bases[x]
        usedPath[x] = true
        if matches[x] < 0 then exit end if
        x = paths[matches[x]]
    end while
    while true do
        y = bases[y]
        if usedPath[y] then exit end if
        y = paths[matches[y]]
    end while
    return y
end function

// mark path from v up to base0, setting parents to x
procedure markPath(integer v, base0, x)
    while bases[v] != base0 do
        integer mv := matches[v]
        blossom[bases[v]] = true
        blossom[bases[mv]] = true
        paths[v] = x
        x = mv
        v = paths[x]
    end while
end procedure

// attempt to find an augmenting path from root
function findPath(sequence adj, integer root)
    // reset BFS state
    paths = repeat(-1,n)
    bases = tagset(n)

    sequence queue = {root},
              used = repeat(false,n)
    used[root] = true

    integer qi := 1
    while qi <= length(queue) do
        integer v := queue[qi]
        qi += 1
        for u in adj[v] do
            if bases[v] != bases[u] and matches[v] != u then
                // blossom found
                if u == root 
                or (matches[u] >= 0 and paths[matches[u]] >= 0) then
                    integer curbase := lca(v,u)
                    blossom = repeat(false,n)
                    markPath(v,curbase,u)
                    markPath(u,curbase,v)
                    for i=1 to n do
                        if blossom[bases[i]] then
                            bases[i] = curbase
                            if not used[i] then
                                used[i] = true
                                queue &= i
                            end if
                        end if
                    end for
                elsif paths[u] < 0 then
                    // extend tree
                    paths[u] = v
                    if matches[u] < 0 then
                        // augmenting path found
                        integer cur := u
                        while cur > 0 do
                            integer prev := paths[cur],
                                     nxt := -1
                            if prev > 0 then
                                nxt = matches[prev]
                                matches[cur] = prev
                                matches[prev] = cur
                            end if
                            cur = nxt
                        end while
                        return true
                    end if
                    // enqueue matched partner
                    integer mu := matches[u]
                    if not used[mu] then
                        used[mu] = true
                        queue &= mu
                    end if
                end if
            end if
        end for
    end while
    return false
end function

// Solve returns the matching array and the size of the matching
function solve(sequence adj)
    integer res := 0
    matches = repeat(-1,n)
    for v=1 to n do
        if matches[v] < 0 then
            if findPath(adj,v) then
                res += 1
            end if
        end if
    end for
    return res
end function

// Example: 5-cycle 0–1–2–3–4–0
n = 5
sequence adj := repeat({},n)
for e in {{1,2},{2,3},{3,4},{4,5},{5,1}} do
    integer {u,v} := e
    adj[u] = append(adj[u], v)
    adj[v] = append(adj[v], u)
end for

integer size := solve(adj)

printf(1,"Maximum matching size: %d\n", size)
printf(1,"Matched pairs:\n")
for u,v in matches do
    if v>=1 and u<v then
        printf(1,"  %d - %d\n", {u,v})
    end if
end for
Output:
Maximum matching size: 2
Matched pairs:
  1 - 2
  3 - 4

Python

from collections import deque

def max_matching(adj):
    """
    Finds maximum matching in a general undirected graph using Edmonds' Blossom algorithm.
    Input: adj — list of lists, adj[u] is the list of neighbors of u (0-indexed).
    Returns: match, size
      match — list where match[u] = v if u–v is in the matching, or -1
      size  — number of matched edges
    """
    n = len(adj)
    match = [-1] * n       # match[u] = v if u is matched to v
    p     = [-1] * n       # parent in the alternating tree
    base  = list(range(n)) # base[u] = base vertex of blossom containing u
    q     = deque()        # queue for BFS
    used  = [False] * n    # whether vertex is in the tree
    blossom = [False] * n  # helper array for marking blossoms

    def lca(a, b):
        """Find least common ancestor of a and b in the forest of alternating tree."""
        used_path = [False] * n
        while True:
            a = base[a]
            used_path[a] = True
            if match[a] == -1: break
            a = p[match[a]]
        while True:
            b = base[b]
            if used_path[b]:
                return b
            b = p[match[b]]

    def mark_path(v, b, x):
        """Mark vertices along the path from v to base b, setting their parent to x."""
        while base[v] != b:
            blossom[base[v]] = blossom[base[match[v]]] = True
            p[v] = x
            x = match[v]
            v = p[x]

    def find_path(root):
        """Try to find an augmenting path starting from root."""
        # reset
        used[:] = [False] * n
        p[:]    = [-1] * n
        for i in range(n):
            base[i] = i
        q.clear()

        used[root] = True
        q.append(root)

        while q:
            v = q.popleft()
            for u in adj[v]:
                # two cases to skip
                if base[v] == base[u] or match[v] == u:
                    continue
                # found a blossom or an odd cycle edge
                if u == root or (match[u] != -1 and p[match[u]] != -1):
                    curbase = lca(v, u)
                    blossom[:] = [False] * n
                    mark_path(v, curbase, u)
                    mark_path(u, curbase, v)
                    # contract blossom
                    for i in range(n):
                        if blossom[base[i]]:
                            base[i] = curbase
                            if not used[i]:
                                used[i] = True
                                q.append(i)
                # otherwise extend the alternating tree
                elif p[u] == -1:
                    p[u] = v
                    if match[u] == -1:
                        # augmenting path found: flip matches along the path
                        curr = u
                        while curr != -1:
                            prev = p[curr]
                            nxt  = match[prev] if prev != -1 else -1
                            match[curr] = prev
                            match[prev] = curr
                            curr = nxt
                        return True
                    # else continue BFS from the matched partner
                    used[match[u]] = True
                    q.append(match[u])
        return False

    # Main loop: try to grow matching by finding augmenting paths
    res = 0
    for v in range(n):
        if match[v] == -1:
            if find_path(v):
                res += 1

    return match, res


if __name__ == "__main__":
    # Example: 5‑cycle (odd cycle)
    # Vertices: 0–1–2–3–4–0
    n = 5
    edges = [(0,1), (1,2), (2,3), (3,4), (4,0)]
    adj = [[] for _ in range(n)]
    for u, v in edges:
        adj[u].append(v)
        adj[v].append(u)

    match, msize = max_matching(adj)
    print(f"Maximum matching size: {msize}")
    print("Matched pairs:")
    seen = set()
    for u, v in enumerate(match):
        if v != -1 and (v, u) not in seen:
            print(f"  {u}{v}")
            seen.add((u, v))
Output:
Maximum matching size: 2
Matched pairs:
  0 – 1
  2 – 3

Raku

Translation of: Phix
Translation of: Python
# 20250512 Raku programming solution

# least common ancestor of x and y in the alternating forest
sub lca($x is copy, $y is copy, @bases, @matches, @paths, $n) {
   my @usedPath = False xx $n;
   loop {
      @usedPath[$x = @bases[$x]] = True;
      @matches[$x] < 0 ?? ( last ) !! $x = @paths[@matches[$x]]
   }
   loop {
      @usedPath[$y = @bases[$y]].Bool ?? ( last ) !! $y = @paths[@matches[$y]]     
   }
   return $y;
}

# mark path from v up to base0, setting parents to x
sub markPath($v is copy, $base0, $x is copy, @bases, @matches, @paths, @blossom) {
   while @bases[$v] != $base0 {
      my $mv = @matches[$v];
      @blossom[@bases[$mv]] = @blossom[@bases[$v]] = True;
      @paths[$v] = $x;
      $v = @paths[ $x = $mv ];
   }
}

# attempt to find an augmenting path from root
sub findPath(@adj, $root, $n, @matches is copy) {
   my @paths = -1 xx $n;
   my @bases = ^$n;
   my @queue = ($root);
   my (@used, @blossom) is default( False );
   @used[$root] = True;

   while my $v = @queue.shift {
      for @adj[$v].flat -> $u {
         next unless @bases[$v] != @bases[$u] && @matches[$v] != $u;
         if $u == $root || (@matches[$u] != -1 && @paths[@matches[$u]] != -1) {
            # Found a blossom; contract it
            my $curbase = lca($v, $u, @bases, @matches, @paths, $n);
            markPath($v, $curbase, $u, @bases, @matches, @paths, @blossom);
            markPath($u, $curbase, $v, @bases, @matches, @paths, @blossom);
            @queue.push: gather for ^$n -> $i {
               if @blossom[@bases[$i]] {
                  @bases[$i] = $curbase;
                  unless @used[$i] { @used[take $i] = True }
               }
            }
         } elsif @paths[$u] == -1 { # extend tree
            @paths[$u] = $v;
            if @matches[$u] == -1 { # augmenting path found
               my $v = $u;
               while $v != -1 {
                  my $prev = @paths[$v];
                  my $next = ( $prev != -1 ) ?? @matches[$prev] !! -1;
                  @matches[$v] = $prev;
                  @matches[$prev] = $v if $prev != -1;
                  $v = $next;
               }
               return (@matches, True);
            }
            @used[@matches[$u]] = True;
            @queue.push: @matches[$u];
         }
      }
   }
   return (@matches, False);
}

# find and return the matching array and the size of the matching
sub solve(@adj) {
   my @matches = -1 xx ( my $n = @adj.elems ); 
   my $res = 0;

   for ^$n -> $v {
      if @matches[$v] == -1 {
         my ($new_matches, $found) = findPath(@adj, $v, $n, @matches);
         if $found {
            @matches = @$new_matches;
            $res++;
         }
      }
   }
   return (@matches, $res);
}

# Example: 5-cycle (0–1–2–3–4–0)
my $n = 5;
my @edges = (0,1), (1,2), (2,3), (3,4), (4,0);
my @adj = [ [] xx $n ];
for @edges -> ($u, $v) {
   @adj[$u].push($v);
   @adj[$v].push($u);
}

my ($matches, $msize) = solve(@adj);
say "Maximum matching size: $msize";
say "Matched pairs:";
for $matches.kv -> $u, $v { say "  $u – $v" if $v != -1 && $u < $v }

You may Attempt This Online!

Rust

Translation of: C++
use std::collections::VecDeque;

struct Blossom {
    n: usize,
    adj: Vec<Vec<usize>>,
    matching: Vec<Option<usize>>,  // matching[v] = Some(u) if v–u is matched
    parent: Vec<Option<usize>>,    // parent in the alternating tree
    base: Vec<usize>,              // base[v] = base vertex of blossom containing v
    used: Vec<bool>,               // used[v] = whether v is in the BFS tree
    blossom: Vec<bool>,            // helper array for marking blossoms
    q: VecDeque<usize>,            // BFS queue
}

impl Blossom {
    fn new(adj: Vec<Vec<usize>>) -> Self {
        let n = adj.len();
        Blossom {
            n,
            adj,
            matching: vec![None; n],
            parent: vec![None; n],
            base: (0..n).collect(),
            used: vec![false; n],
            blossom: vec![false; n],
            q: VecDeque::new(),
        }
    }

    // Find least common ancestor of a and b in the forest of alternating tree.
    fn lca(&self, mut a: usize, mut b: usize) -> usize {
        let mut used_path = vec![false; self.n];
        // climb from a
        loop {
            a = self.base[a];
            used_path[a] = true;
            if let Some(ma) = self.matching[a] {
                if let Some(pa) = self.parent[ma] {
                    a = pa;
                    continue;
                }
            }
            break;
        }
        // climb from b until we hit a marked vertex
        loop {
            b = self.base[b];
            if used_path[b] {
                return b;
            }
            if let Some(mb) = self.matching[b] {
                if let Some(pb) = self.parent[mb] {
                    b = pb;
                    continue;
                }
            }
            // In a valid alternating forest this should always terminate
        }
    }

    // Mark vertices along the path from v to base b, setting their parent to x.
    fn mark_path(&mut self, mut v: usize, b: usize, mut x: usize) {
        while self.base[v] != b {
            let mv = self.matching[v].unwrap();
            self.blossom[self.base[v]] = true;
            self.blossom[self.base[mv]] = true;
            self.parent[v] = Some(x);
            x = mv;
            v = self.parent[x].unwrap();
        }
    }

    // Try to find an augmenting path starting from root.
    fn find_path(&mut self, root: usize) -> bool {
        // Reset BFS state
        self.used.fill(false);
        self.parent.fill(None);
        for i in 0..self.n {
            self.base[i] = i;
        }
        self.q.clear();

        self.used[root] = true;
        self.q.push_back(root);

        while let Some(v) = self.q.pop_front() {
            // clone neighbors to avoid borrowing self.adj across the BFS loop
            let neighbors = self.adj[v].clone();
            for u in neighbors {
                if self.base[v] == self.base[u] || self.matching[v] == Some(u) {
                    continue;
                }
                // Found a blossom
                if u == root
                    || (self.matching[u].is_some()
                        && self.parent[self.matching[u].unwrap()].is_some())
                {
                    let curbase = self.lca(v, u);
                    self.blossom.fill(false);
                    self.mark_path(v, curbase, u);
                    self.mark_path(u, curbase, v);
                    for i in 0..self.n {
                        if self.blossom[self.base[i]] {
                            self.base[i] = curbase;
                            if !self.used[i] {
                                self.used[i] = true;
                                self.q.push_back(i);
                            }
                        }
                    }
                }
                // Otherwise, try to extend the alternating tree
                else if self.parent[u].is_none() {
                    self.parent[u] = Some(v);
                    // If u is free, we've found an augmenting path
                    if self.matching[u].is_none() {
                        let mut cur = u;
                        while let Some(prev) = self.parent[cur] {
                            let next = self.matching[prev];
                            self.matching[cur] = Some(prev);
                            self.matching[prev] = Some(cur);
                            if let Some(nxt) = next {
                                cur = nxt;
                            } else {
                                break;
                            }
                        }
                        return true;
                    }
                    // Otherwise enqueue the matched partner
                    let mu = self.matching[u].unwrap();
                    if !self.used[mu] {
                        self.used[mu] = true;
                        self.q.push_back(mu);
                    }
                }
            }
        }
        false
    }

    // Main solver: returns (matching, size_of_matching)
    fn solve(&mut self) -> (Vec<Option<usize>>, usize) {
        let mut res = 0;
        for v in 0..self.n {
            if self.matching[v].is_none() && self.find_path(v) {
                res += 1;
            }
        }
        (self.matching.clone(), res)
    }
}

fn main() {
    // Example: 5‑cycle (odd cycle) 0–1–2–3–4–0
    let n = 5;
    let edges = vec![(0,1), (1,2), (2,3), (3,4), (4,0)];
    let mut adj = vec![Vec::new(); n];
    for &(u, v) in &edges {
        adj[u].push(v);
        adj[v].push(u);
    }

    let mut blossom = Blossom::new(adj);
    let (matching, msize) = blossom.solve();

    println!("Maximum matching size: {}", msize);
    println!("Matched pairs:");
    for u in 0..n {
        if let Some(v) = matching[u] {
            if u < v {
                println!("  {} – {}", u, v);
            }
        }
    }
}
Output:
Maximum matching size: 2
Matched pairs:
  0 – 1
  2 – 3

Wren

Translation of: Python
Library: Wren-queue
Library: Wren-set
import "./queue" for Deque
import "./set" for Set

/* Finds maximum matching in a general undirected graph using Edmonds' Blossom algorithm.
   Input: adj — list of lists, adj[u] is the list of neighbors of u (0-indexed).
   Returns: match, size
   match — list where match[u] = v if u–v is in the matching, or -1
   size  — number of matched edges
*/
var maxMatching = Fn.new { |adj|
    var n = adj.count
    var match = List.filled(n, -1)       // match[u] = v if u is matched to v
    var p = List.filled(n, -1)           // parent in the alternating tree
    var base = (0...n).toList            // base[u] = base vertex of blossom containing u
    var q = Deque.new()                  // queue for BFS
    var used = List.filled(n, false)     // whether vertex is in the tree
    var blossom = List.filled(n, false)  // helper array for marking blossoms

    // Find least common ancestor of a and b in the forest of alternating tree.
    var lca = Fn.new { |a, b|
        var usedPath = List.filled(n, false)
        while (true) {
            a = base[a]
            usedPath[a] = true
            if (match[a] == -1) break
            a = p[match[a]]
        }
        while (true) {
            b = base[b]
            if (usedPath[b]) return b
            b = p[match[b]]
        }
    }

    // Mark vertices along the path from v to base b, setting their parent to x.
    var markPath = Fn.new { |v, b, x|
        while (base[v] != b) {
            blossom[base[v]] = blossom[base[match[v]]] = true
            p[v] = x
            x = match[v]
            v = p[x]
        }
    }

    // Try to find an augmenting path starting from root.
    var findPath = Fn.new { |root|
        // reset
        used = List.filled(n, false)
        p = List.filled(n, -1)
        for (i in 0...n) base[i] = i
        q.clear()

        used[root] = true
        q.pushBack(root)

        while (!q.isEmpty) {
            var v = q.popFront()
            for (u in adj[v]) {
                // two cases to skip
                if (base[v] == base[u] || match[v] == u) continue
                // found a blossom or an odd cycle edge
                if (u == root || (match[u] != -1 && p[match[u]] != -1)) {
                    var curbase = lca.call(v, u)
                    blossom = List.filled(n, false)
                    markPath.call(v, curbase, u)
                    markPath.call(u, curbase, v)
                    // contract blossom
                    for (i in 0...n) {
                        if (blossom[base[i]]) {
                            base[i] = curbase
                            if (!used[i]) {
                                used[i] = true
                                q.pushBack(i)
                            }
                        }
                    }
                // otherwise extend the alternating tree
                } else if (p[u] == -1) {
                    p[u] = v
                    if (match[u] == -1) {
                        // augmenting path found: flip matches along the path
                        var curr = u
                        while (curr != -1) {
                            var prev = p[curr]
                            var next = (prev != -1) ? match[prev] : -1
                            match[curr] = prev
                            match[prev] = curr
                            curr = next
                        }
                        return true
                    }
                    // else continue BFS from the matched partner
                    used[match[u]] = true
                    q.pushBack(match[u])
                }
            }
        }
        return false
    }

    // Main loop: try to grow matching by finding augmenting paths.
    var res = 0
    for (v in 0...n) {
        if (match[v] == -1) {
            if (findPath.call(v)) res = res + 1
        }
    }
    return [match, res]
}

// Example: 5‑cycle (odd cycle)
// Vertices: 0–1–2–3–4–0
var n = 5
var edges = [ [0, 1], [1, 2], [2, 3], [3, 4], [4, 0] ]
var adj = List.filled(n, null)
for (i in 0...n) adj[i] = []
for (e in edges) {
    var u = e[0]
    var v = e[1]
    adj[u].add(v)
    adj[v].add(u)
}
var res = maxMatching.call(adj)
var match = res[0]
var msize = res[1]
System.print("Maximum matching size: %(msize)")
System.print("Matched pairs:")
var seen = Set.new()
var u = -1
for (v in match) {
    u = u + 1
    if (v != -1 && !seen.contains([v, u].toString)) {
        System.print("  %(u)%(v)")
        seen.add([u, v].toString)
    }
}
Output:
Maximum matching size: 2
Matched pairs:
  0 – 1
  2 – 3


Zig

Translation of: Rust
const std = @import("std");

const Blossom = struct {
    n: usize,
    adj: [][]usize,
    matching: []?usize,  // matching[v] = u if v--u is matched
    parent: []?usize,    // parent in the alternating tree
    base: []usize,       // base[v] = base vertex of blossom containing v
    used: []bool,        // used[v] = whether v is in the BFS tree
    blossom: []bool,     // helper array for marking blossoms
    q: std.fifo.LinearFifo(usize, .Dynamic),  // BFS queue
    allocator: std.mem.Allocator,

    fn init(allocator: std.mem.Allocator, adj: [][]usize) !Blossom {
        const n = adj.len;
        const matching = try allocator.alloc(?usize, n);
        const parent = try allocator.alloc(?usize, n);
        var base = try allocator.alloc(usize, n);
        const used = try allocator.alloc(bool, n);
        const blossom = try allocator.alloc(bool, n);
        const q = std.fifo.LinearFifo(usize, .Dynamic).init(allocator);

        @memset(matching, null);
        @memset(parent, null);
        for (0..n) |i| {
            base[i] = i;
        }
        @memset(used, false);
        @memset(blossom, false);

        return Blossom{
            .n = n,
            .adj = adj,
            .matching = matching,
            .parent = parent,
            .base = base,
            .used = used,
            .blossom = blossom,
            .q = q,
            .allocator = allocator,
        };
    }

    fn deinit(self: *Blossom) void {
        self.allocator.free(self.matching);
        self.allocator.free(self.parent);
        self.allocator.free(self.base);
        self.allocator.free(self.used);
        self.allocator.free(self.blossom);
        self.q.deinit();
    }

    // Find least common ancestor of a and b in the forest of alternating tree.
    fn lca(self: *const Blossom, a_in: usize, b_in: usize) usize {
        var a = a_in;
        var b = b_in;
        var used_path = self.allocator.alloc(bool, self.n) catch unreachable;
        defer self.allocator.free(used_path);
        @memset(used_path, false);

        // climb from a
        while (true) {
            a = self.base[a];
            used_path[a] = true;
            if (self.matching[a]) |ma| {
                if (self.parent[ma]) |pa| {
                    a = pa;
                    continue;
                }
            }
            break;
        }

        // climb from b until we hit a marked vertex
        while (true) {
            b = self.base[b];
            if (used_path[b]) {
                return b;
            }
            if (self.matching[b]) |mb| {
                if (self.parent[mb]) |pb| {
                    b = pb;
                    continue;
                }
            }
            // In a valid alternating forest this should always terminate
            unreachable;
        }
    }

    // Mark vertices along the path from v to base b, setting their parent to x.
    fn markPath(self: *Blossom, v_in: usize, b: usize, x_in: usize) void {
        var v = v_in;
        var x = x_in;
        while (self.base[v] != b) {
            const mv = self.matching[v].?;
            self.blossom[self.base[v]] = true;
            self.blossom[self.base[mv]] = true;
            self.parent[v] = x;
            x = mv;
            v = self.parent[x].?;
        }
    }

    // Try to find an augmenting path starting from root.
    fn findPath(self: *Blossom, root: usize) bool {
        // Reset BFS state
        @memset(self.used, false);
        @memset(self.parent, null);
        for (0..self.n) |i| {
            self.base[i] = i;
        }
        self.q.head = 0;
        self.q.count = 0;

        self.used[root] = true;
        self.q.writeItem(root) catch unreachable;

        while (self.q.readItem()) |v| {
            for (self.adj[v]) |u| {
                if (self.base[v] == self.base[u] or self.matching[v] == u) {
                    continue;
                }
                // Found a blossom
                if (u == root or 
                    (self.matching[u] != null and self.parent[self.matching[u].?] != null)) {
                    const curbase = self.lca(v, u);
                    @memset(self.blossom, false);
                    self.markPath(v, curbase, u);
                    self.markPath(u, curbase, v);
                    for (0..self.n) |i| {
                        if (self.blossom[self.base[i]]) {
                            self.base[i] = curbase;
                            if (!self.used[i]) {
                                self.used[i] = true;
                                self.q.writeItem(i) catch unreachable;
                            }
                        }
                    }
                }
                // Otherwise, try to extend the alternating tree
                else if (self.parent[u] == null) {
                    self.parent[u] = v;
                    // If u is free, we've found an augmenting path
                    if (self.matching[u] == null) {
                        var cur = u;
                        while (self.parent[cur]) |prev| {
                            const next = self.matching[prev];
                            self.matching[cur] = prev;
                            self.matching[prev] = cur;
                            cur = next orelse break;
                        }
                        return true;
                    }
                    // Otherwise enqueue the matched partner
                    const mu = self.matching[u].?;
                    if (!self.used[mu]) {
                        self.used[mu] = true;
                        self.q.writeItem(mu) catch unreachable;
                    }
                }
            }
        }
        return false;
    }

    // Main solver: returns (matching, size_of_matching)
    fn solve(self: *Blossom) struct { matching: []?usize, size: usize } {
        var res: usize = 0;
        for (0..self.n) |v| {
            if (self.matching[v] == null and self.findPath(v)) {
                res += 1;
            }
        }
        
        const result_matching = self.allocator.alloc(?usize, self.n) catch unreachable;
        @memcpy(result_matching, self.matching);
        
        return .{ .matching = result_matching, .size = res };
    }
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Example: 5‑cycle (odd cycle) 0--1--2--3--4--0
    const n: usize = 5;
    const edges = [_][2]usize{
        .{0, 1}, .{1, 2}, .{2, 3}, .{3, 4}, .{4, 0}
    };
    
    var adj = try allocator.alloc([]usize, n);
    defer {
        for (adj) |neighbors| {
            allocator.free(neighbors);
        }
        allocator.free(adj);
    }
    
    // Initialize empty adjacency lists
    for (0..n) |i| {
        adj[i] = try allocator.alloc(usize, 0);
    }
    
    // Count neighbors for each vertex
    var counts = try allocator.alloc(usize, n);
    defer allocator.free(counts);
    @memset(counts, 0);
    
    for (edges) |edge| {
        counts[edge[0]] += 1;
        counts[edge[1]] += 1;
    }
    
    // Allocate space for neighbors
    for (0..n) |i| {
        allocator.free(adj[i]);
        adj[i] = try allocator.alloc(usize, counts[i]);
        counts[i] = 0; // Reset to use as index
    }
    
    // Fill adjacency lists
    for (edges) |edge| {
        const u = edge[0];
        const v = edge[1];
        adj[u][counts[u]] = v;
        counts[u] += 1;
        adj[v][counts[v]] = u;
        counts[v] += 1;
    }

    var blossom = try Blossom.init(allocator, adj);
    defer blossom.deinit();
    
    const result = blossom.solve();
    defer allocator.free(result.matching);

    const stdout = std.io.getStdOut().writer();
    try stdout.print("Maximum matching size: {d}\n", .{result.size});
    try stdout.print("Matched pairs:\n", .{});
    
    for (0..n) |u| {
        if (result.matching[u]) |v| {
            if (u < v) {
                try stdout.print("  {d} -- {d}\n", .{u, v});
            }
        }
    }
}
Output:
Maximum matching size: 2
Matched pairs:
  0 -- 1
  2 -- 3
Cookies help us deliver our services. By using our services, you agree to our use of cookies.