Skip to main content

god_graph/algorithms/
matching.rs

1//! 匹配算法模块
2//!
3//! 包含最大匹配算法:Hopcroft-Karp(二分图)、Blossom(一般图)
4
5use crate::edge::EdgeIndex;
6use crate::graph::traits::{GraphBase, GraphQuery};
7use crate::graph::Graph;
8use std::collections::VecDeque;
9
10/// Hopcroft-Karp 二分图最大匹配算法
11///
12/// # 复杂度
13/// - 时间:O(E * sqrt(V))
14/// - 空间:O(V)
15///
16/// # 参数
17/// * `graph` - 二分图
18/// * `left_nodes` - 左部节点的索引列表
19/// * `right_nodes` - 右部节点的索引列表
20///
21/// # 返回
22/// 匹配对列表,每个元素是 (左部节点索引,右部节点索引)
23///
24/// # 示例
25/// ```
26/// use god_gragh::prelude::*;
27/// use god_gragh::algorithms::matching::hopcroft_karp;
28///
29/// let graph = GraphBuilder::undirected()
30///     .with_nodes(vec!["L1", "L2", "R1", "R2"])
31///     .with_edges(vec![(0, 2, 1.0), (0, 3, 1.0), (1, 2, 1.0)])
32///     .build()
33///     .unwrap();
34///
35/// let left = vec![0, 1];  // L1, L2
36/// let right = vec![2, 3]; // R1, R2
37/// let matching = hopcroft_karp(&graph, &left, &right);
38/// assert!(!matching.is_empty());
39/// ```
40pub fn hopcroft_karp<T>(
41    graph: &Graph<T, impl Clone>,
42    left_nodes: &[usize],
43    right_nodes: &[usize],
44) -> Vec<(usize, usize)> {
45    let n_left = left_nodes.len();
46    let n_right = right_nodes.len();
47
48    if n_left == 0 || n_right == 0 {
49        return vec![];
50    }
51
52    // 构建邻接表
53    let node_indices: Vec<_> = graph.nodes().map(|n| n.index()).collect();
54    let _index_to_pos: std::collections::HashMap<usize, usize> = left_nodes
55        .iter()
56        .enumerate()
57        .map(|(i, &idx)| (idx, i))
58        .collect();
59
60    let mut adj: Vec<Vec<usize>> = vec![vec![]; n_left];
61
62    for (left_pos, &left_idx) in left_nodes.iter().enumerate() {
63        if left_idx < node_indices.len() {
64            for neighbor in graph.neighbors(node_indices[left_idx]) {
65                if let Some(right_pos) = right_nodes.iter().position(|&r| r == neighbor.index()) {
66                    adj[left_pos].push(right_pos);
67                }
68            }
69        }
70    }
71
72    // 匹配数组
73    let mut pair_left = vec![None; n_left];
74    let mut pair_right = vec![None; n_right];
75    let mut dist = vec![0; n_left];
76
77    // BFS 寻找增广路
78    fn bfs(
79        adj: &[Vec<usize>],
80        pair_left: &[Option<usize>],
81        pair_right: &[Option<usize>],
82        dist: &mut [usize],
83    ) -> bool {
84        let mut queue = VecDeque::new();
85        for (i, &p) in pair_left.iter().enumerate() {
86            if p.is_none() {
87                dist[i] = 0;
88                queue.push_back(i);
89            } else {
90                dist[i] = usize::MAX;
91            }
92        }
93
94        let mut found = false;
95        while let Some(u) = queue.pop_front() {
96            for &v in &adj[u] {
97                if let Some(next) = pair_right.get(v).and_then(|&x| x) {
98                    if dist[next] == usize::MAX {
99                        dist[next] = dist[u] + 1;
100                        queue.push_back(next);
101                    }
102                } else {
103                    found = true;
104                }
105            }
106        }
107        found
108    }
109
110    // DFS 寻找增广路
111    fn dfs(
112        u: usize,
113        adj: &[Vec<usize>],
114        pair_left: &mut [Option<usize>],
115        pair_right: &mut [Option<usize>],
116        dist: &mut [usize],
117    ) -> bool {
118        for &v in &adj[u] {
119            if let Some(next) = pair_right.get(v).and_then(|&x| x) {
120                if dist[next] == dist[u] + 1 && dfs(next, adj, pair_left, pair_right, dist) {
121                    pair_left[u] = Some(v);
122                    pair_right[v] = Some(u);
123                    return true;
124                }
125            } else {
126                pair_left[u] = Some(v);
127                pair_right[v] = Some(u);
128                return true;
129            }
130        }
131        dist[u] = usize::MAX;
132        false
133    }
134
135    // 主循环
136    while bfs(&adj, &pair_left, &pair_right, &mut dist) {
137        for i in 0..n_left {
138            if pair_left[i].is_none() {
139                dfs(i, &adj, &mut pair_left, &mut pair_right, &mut dist);
140            }
141        }
142    }
143
144    // 构建结果
145    left_nodes
146        .iter()
147        .enumerate()
148        .filter_map(|(i, &left_idx)| {
149            pair_left[i].map(|right_pos| (left_idx, right_nodes[right_pos]))
150        })
151        .collect()
152}
153
154/// Blossom 算法(一般图最大匹配)
155///
156/// 使用带花树算法求解一般图的最大匹配
157///
158/// # 复杂度
159/// - 时间:O(V * E)
160/// - 空间:O(V)
161///
162/// # 参数
163/// * `graph` - 无向图
164///
165/// # 返回
166/// 匹配边索引列表
167///
168/// # 示例
169/// ```
170/// use god_gragh::prelude::*;
171/// use god_gragh::algorithms::matching::blossom;
172///
173/// let graph = GraphBuilder::undirected()
174///     .with_nodes(vec!["A", "B", "C", "D"])
175///     .with_edges(vec![(0, 1, 1.0), (2, 3, 1.0)])
176///     .build()
177///     .unwrap();
178///
179/// let matching = blossom(&graph);
180/// assert_eq!(matching.len(), 2);
181/// ```
182pub fn blossom<T>(graph: &Graph<T, impl Clone>) -> Vec<EdgeIndex> {
183    let n = graph.node_count();
184    if n == 0 {
185        return vec![];
186    }
187
188    let node_indices: Vec<_> = graph.nodes().map(|n| n.index()).collect();
189    let mut match_node: Vec<Option<usize>> = vec![None; n]; // 每个节点的匹配对象
190    let mut used_edges = Vec::new();
191
192    // 尝试为每个未匹配节点寻找增广路
193    for start in 0..n {
194        if match_node[start].is_some() {
195            continue;
196        }
197
198        // BFS 寻找增广路
199        let mut parent: Vec<Option<usize>> = vec![None; n];
200        let mut visited = vec![false; n];
201        let mut queue = VecDeque::new();
202
203        queue.push_back(start);
204        visited[start] = true;
205
206        while let Some(u) = queue.pop_front() {
207            for neighbor in graph.neighbors(node_indices[u]) {
208                let v = neighbor.index();
209                if v >= n {
210                    continue;
211                }
212
213                if !visited[v] {
214                    visited[v] = true;
215                    parent[v] = Some(u);
216
217                    if let Some(matched) = match_node.get(v).and_then(|&x| x) {
218                        // v 已匹配,继续搜索
219                        if !visited[matched] {
220                            visited[matched] = true;
221                            parent[matched] = Some(v);
222                            queue.push_back(matched);
223                        }
224                    } else {
225                        // 找到增广路
226                        let mut curr = v;
227                        let mut prev = u;
228                        while let Some(p) = parent[prev] {
229                            match_node[prev] = Some(curr);
230                            match_node[curr] = Some(prev);
231                            curr = p;
232                            prev = parent[p].unwrap();
233                        }
234                        match_node[start] = Some(curr);
235                        match_node[curr] = Some(start);
236                        break;
237                    }
238                }
239            }
240        }
241    }
242
243    // 收集匹配边
244    let mut seen = std::collections::HashSet::new();
245    for (u, &v_opt) in match_node.iter().enumerate() {
246        if let Some(v) = v_opt {
247            if u < v && !seen.contains(&(u, v)) {
248                seen.insert((u, v));
249                // 找到对应的边索引
250                if u < node_indices.len() {
251                    for edge_idx in graph.incident_edges(node_indices[u]) {
252                        if let Ok((src, tgt)) = graph.edge_endpoints(edge_idx) {
253                            if (src.index() == u && tgt.index() == v)
254                                || (src.index() == v && tgt.index() == u)
255                            {
256                                used_edges.push(edge_idx);
257                                break;
258                            }
259                        }
260                    }
261                }
262            }
263        }
264    }
265
266    used_edges
267}
268
269#[cfg(test)]
270mod tests {
271    use super::*;
272    use crate::graph::builders::GraphBuilder;
273
274    #[test]
275    fn test_hopcroft_karp_basic() {
276        let graph = GraphBuilder::undirected()
277            .with_nodes(vec!["L1", "L2", "R1", "R2"])
278            .with_edges(vec![(0, 2, 1.0), (0, 3, 1.0), (1, 2, 1.0)])
279            .build()
280            .unwrap();
281
282        let left = vec![0, 1];
283        let right = vec![2, 3];
284        let matching = hopcroft_karp(&graph, &left, &right);
285        assert!(!matching.is_empty());
286    }
287
288    #[test]
289    fn test_hopcroft_karp_empty() {
290        let graph: Graph<&str, f64> = GraphBuilder::undirected()
291            .with_nodes(vec!["L1", "R1"])
292            .build()
293            .unwrap();
294
295        let left = vec![0];
296        let right = vec![1];
297        let matching = hopcroft_karp(&graph, &left, &right);
298        assert!(matching.is_empty()); // 没有边,匹配为空
299    }
300
301    #[test]
302    fn test_hopcroft_karp_perfect_matching() {
303        // 完美匹配:每个左部节点都匹配到一个右部节点
304        let graph: Graph<&str, f64> = GraphBuilder::undirected()
305            .with_nodes(vec!["L1", "L2", "R1", "R2"])
306            .with_edges(vec![(0, 2, 1.0), (1, 3, 1.0)])
307            .build()
308            .unwrap();
309
310        let left = vec![0, 1];
311        let right = vec![2, 3];
312        let matching = hopcroft_karp(&graph, &left, &right);
313        assert_eq!(matching.len(), 2); // 完美匹配
314    }
315
316    #[test]
317    fn test_hopcroft_karp_single_node() {
318        let graph: Graph<&str, f64> = GraphBuilder::undirected()
319            .with_nodes(vec!["L1"])
320            .build()
321            .unwrap();
322
323        let left = vec![0];
324        let right = vec![];
325        let matching = hopcroft_karp(&graph, &left, &right);
326        assert!(matching.is_empty());
327    }
328
329    #[test]
330    fn test_blossom_basic() {
331        let graph: Graph<&str, f64> = GraphBuilder::undirected()
332            .with_nodes(vec!["A", "B", "C", "D"])
333            .with_edges(vec![(0, 1, 1.0), (2, 3, 1.0)])
334            .build()
335            .unwrap();
336
337        let matching = blossom(&graph);
338        assert!(!matching.is_empty());
339    }
340
341    #[test]
342    fn test_blossom_odd_cycle() {
343        // 测试花算法处理奇环的能力
344        // 5 个节点的环:最大匹配应该是 2 条边
345        let graph: Graph<i32, f64> = GraphBuilder::undirected()
346            .with_nodes(vec![1, 2, 3, 4, 5])
347            .with_edges(vec![
348                (0, 1, 1.0),
349                (1, 2, 1.0),
350                (2, 3, 1.0),
351                (3, 4, 1.0),
352                (4, 0, 1.0),
353            ])
354            .build()
355            .unwrap();
356
357        let matching = blossom(&graph);
358        assert_eq!(matching.len(), 2); // 5 个节点的环最大匹配为 2
359    }
360
361    #[test]
362    fn test_blossom_empty_graph() {
363        let graph: Graph<&str, f64> = GraphBuilder::undirected()
364            .with_nodes(vec!["A", "B", "C"])
365            .build()
366            .unwrap();
367
368        let matching = blossom(&graph);
369        assert!(matching.is_empty());
370    }
371
372    #[test]
373    fn test_blossom_single_edge() {
374        let graph: Graph<&str, f64> = GraphBuilder::undirected()
375            .with_nodes(vec!["A", "B"])
376            .with_edges(vec![(0, 1, 1.0)])
377            .build()
378            .unwrap();
379
380        let matching = blossom(&graph);
381        assert_eq!(matching.len(), 1);
382    }
383
384    #[test]
385    fn test_blossom_path() {
386        // 路径图:4 个节点的线性链
387        let graph: Graph<i32, f64> = GraphBuilder::undirected()
388            .with_nodes(vec![1, 2, 3, 4])
389            .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0), (2, 3, 1.0)])
390            .build()
391            .unwrap();
392
393        let matching = blossom(&graph);
394        assert_eq!(matching.len(), 2); // 路径最大匹配为 2 条边
395    }
396
397    #[test]
398    fn test_blossom_complete_graph() {
399        // 完全图 K4:4 个节点,每对节点之间都有边
400        let graph: Graph<i32, f64> = GraphBuilder::undirected()
401            .with_nodes(vec![1, 2, 3, 4])
402            .with_edges(vec![
403                (0, 1, 1.0),
404                (0, 2, 1.0),
405                (0, 3, 1.0),
406                (1, 2, 1.0),
407                (1, 3, 1.0),
408                (2, 3, 1.0),
409            ])
410            .build()
411            .unwrap();
412
413        let matching = blossom(&graph);
414        assert_eq!(matching.len(), 2); // K4 最大匹配为 2 条边
415    }
416}