@graphty/algorithms
Version:
Graph algorithms library for browser environments implemented in TypeScript
127 lines • 4.01 kB
JavaScript
/**
* 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