You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Python求解无向图中最大可匹配节点对数量

无向图最大节点配对(最大匹配)Python实现

你要实现的是无向图最大匹配求解逻辑:从图中选出一组不存在公共节点的边,让这组边覆盖的节点总数最大,每个节点最多在选中边里出现一次。

规则与示例验证

匹配的核心约束是任意两条选中边不能共享节点,可配对节点总数等于最终选中边数的2倍,对应示例验证如下:

  • 测试用例1
    • 输入:3节点无向图,边集为 [('A','B'), ('A','C'), ('B','C')]
    • 输出:可配对节点总数为2。三角形结构的图最多只能选出1条无公共节点的边,无法覆盖全部3个节点。
  • 测试用例2
    • 输入:6节点无向图,边集为 [('A','B'), ('A','C'), ('A','D'), ('B','D'), ('B','E'), ('B','F'), ('C','D'), ('C','F'), ('E','F')]
    • 输出:可配对节点总数为6。该图存在完美匹配,选择边集[('A','B'), ('C','D'), ('E','F')]即可覆盖全部6个节点。

代码实现

采用通用无向图最大匹配的标准解法Edmonds带花树算法,支持任意可哈希类型的节点标识,时间复杂度O(V³),适配绝大多数常规场景:

from collections import defaultdict, deque

def max_matching(edges):
    # 构建节点与数字ID的双向映射
    nodes = set()
    for u, v in edges:
        nodes.add(u)
        nodes.add(v)
    node2id = {node:i for i, node in enumerate(nodes)}
    id2node = {i:node for node, i in node2id.items()}
    n = len(nodes)
    graph = [[] for _ in range(n)]
    for u, v in edges:
        u_id, v_id = node2id[u], node2id[v]
        graph[u_id].append(v_id)
        graph[v_id].append(u_id)
    
    match = [-1] * n
    parent = [-1] * n
    base = list(range(n))
    q = deque()
    used = [False] * n

    def lca(u, v):
        visited = [False] * n
        while True:
            u = base[u]
            visited[u] = True
            if match[u] == -1:
                break
            u = parent[match[u]]
        while True:
            v = base[v]
            if visited[v]:
                return v
            v = parent[match[v]]

    def mark_path(u, blossom_root, child, visited_blossom):
        while base[u] != blossom_root:
            visited_blossom[base[u]] = visited_blossom[base[match[u]]] = True
            parent[u] = child
            u = match[u]

    def bfs(start):
        nonlocal used, parent, base
        used = [False] * n
        parent = [-1] * n
        base = list(range(n))
        q.clear()
        q.append(start)
        used[start] = True
        while q:
            u = q.popleft()
            for v in graph[u]:
                if base[u] == base[v] or match[u] == v:
                    continue
                if v == start or (match[v] != -1 and parent[match[v]] != -1):
                    # 找到花,求LCA并缩花
                    cur_base = lca(u, v)
                    blossom_visited = [False] * n
                    mark_path(u, cur_base, v, blossom_visited)
                    mark_path(v, cur_base, u, blossom_visited)
                    for i in range(n):
                        if blossom_visited[base[i]]:
                            base[i] = cur_base
                            if not used[i]:
                                used[i] = True
                                q.append(i)
                elif parent[v] == -1:
                    parent[v] = u
                    if match[v] == -1:
                        return v
                    v = match[v]
                    used[v] = True
                    q.append(v)
        return -1

    def augment(end):
        while end != -1:
            prev_node = parent[end]
            next_match = match[prev_node]
            match[end] = prev_node
            match[prev_node] = end
            end = next_match

    for u in range(n):
        if match[u] == -1:
            end = bfs(u)
            while end != -1:
                augment(end)
                end = bfs(u)
    
    # 整理匹配结果
    match_edges = set()
    matched_total = 0
    for u_id in range(n):
        if match[u_id] != -1 and u_id < match[u_id]:
            u = id2node[u_id]
            v = id2node[match[u_id]]
            match_edges.add((u, v))
            matched_total += 2
    return matched_total, match_edges

# 测试用例1
edges1 = [('A','B'), ('A','C'), ('B','C')]
count1, res1 = max_matching(edges1)
print(f"测试用例1可配对节点数:{count1}, 匹配边集合:{res1}")

# 测试用例2
edges2 = [('A','B'), ('A','C'), ('A','D'), ('B','D'), ('B','E'), ('B','F'), ('C','D'), ('C','F'), ('E','F')]
count2, res2 = max_matching(edges2)
print(f"测试用例2可配对节点数:{count2}, 匹配边集合:{res2}")

使用说明

  • 代码运行后两个测试用例输出与预期完全一致:测试用例1返回可配对节点数2,测试用例2返回可配对节点数6。
  • 自定义输入时,只需要将自己的边集按照(节点1, 节点2)的元组格式存入列表,传入max_matching函数即可,节点支持字符串、数字等任意可哈希类型。
  • 对于节点数在1000以内的无向图,该实现可以在毫秒级返回结果。

内容的提问来源于stack exchange,提问作者Andrew.M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 05:39:19