UNPKG

@graphty/algorithms

Version:

Graph algorithms library for browser environments implemented in TypeScript

127 lines 4.01 kB
/** * Find maximum matching in a bipartite graph using augmenting path algorithm * (simplified implementation that's more reliable than Hopcroft-Karp for this context) */ export function maximumBipartiteMatching(graph, options = {}) { // If partitions not provided, try to infer them let { leftNodes, rightNodes } = options; if (!leftNodes || !rightNodes) { const partition = bipartitePartition(graph); if (!partition) { throw new Error("Graph is not bipartite"); } leftNodes = partition.left; rightNodes = partition.right; } const matching = new Map(); const matchRight = new Map(); // Right to left matching // Find augmenting paths using DFS const visited = new Set(); const dfs = (u) => { const neighbors = Array.from(graph.neighbors(u)); for (const v of neighbors) { if (!rightNodes.has(v) || visited.has(v)) { continue; } visited.add(v); // If v is unmatched or we can find augmenting path from match of v const matchedNode = matchRight.get(v); if (!matchRight.has(v) || (matchedNode !== undefined && dfs(matchedNode))) { matching.set(u, v); matchRight.set(v, u); return true; } } return false; }; // Try to find augmenting path for each left node let matchingSize = 0; for (const u of leftNodes) { visited.clear(); if (dfs(u)) { matchingSize++; } } return { matching, size: matchingSize, }; } /** * Check if graph is bipartite and return the partition */ export function bipartitePartition(graph) { const color = new Map(); const left = new Set(); const right = new Set(); const nodes = Array.from(graph.nodes()); for (const startNode of nodes) { if (color.has(startNode.id)) { continue; } // BFS to color the component const queue = [startNode.id]; color.set(startNode.id, true); left.add(startNode.id); while (queue.length > 0) { const u = queue.shift(); if (u === undefined) { continue; } const uColor = color.get(u); if (uColor === undefined) { continue; } const neighbors = Array.from(graph.neighbors(u)); for (const v of neighbors) { if (!color.has(v)) { color.set(v, !uColor); if (!uColor) { left.add(v); } else { right.add(v); } queue.push(v); } else if (color.get(v) === uColor) { // Same color as neighbor - not bipartite return null; } } } } return { left, right }; } /** * Simple greedy bipartite matching algorithm * Useful for comparison or when Hopcroft-Karp is overkill */ export function greedyBipartiteMatching(graph, options = {}) { let { leftNodes, rightNodes } = options; if (!leftNodes || !rightNodes) { const partition = bipartitePartition(graph); if (!partition) { throw new Error("Graph is not bipartite"); } leftNodes = partition.left; rightNodes = partition.right; } const matching = new Map(); const matched = new Set(); for (const u of leftNodes) { const neighbors = Array.from(graph.neighbors(u)); for (const v of neighbors) { if (rightNodes.has(v) && !matched.has(v)) { matching.set(u, v); matched.add(v); break; } } } return { matching, size: matching.size, }; } //# sourceMappingURL=bipartite.js.map