如何快速找到连接到二部图子集的所有节点?

Han*_*ave 7 python algorithm math graph-theory

给定一组左节点和右节点,以及它们之间的边(二分图),我需要支持从左节点集到连接到左侧任何内容的右节点子集的快速查找。

如果图形非常稀疏或密集,这很容易。下面是一个示例邻接表实现,它将支持connected_endpoints()稀疏图上的快速调用。O(len(input) * len(result))然而,对于中等数量的连接,尽管涉及的数据大小表明可能存在解决方案,但中间计算看起来像是这样O(len(input) + len(result))

是否存在一种数据结构可以相对快速地支持这 3 个操作(或类似的操作)——添加/删除可能为 O(1),连接边搜索为 O(in+out),给出或采用多对数因子?

from typing import *
from collections import defaultdict

A = TypeVar('A')
B = TypeVar('B')

class Graph(Generic[A, B]):
    def __init__(self):
        self.edges = defaultdict(set)
    
    def set_edge(self, start: A, end: B):
        """Desired: Amortized O(1)"""
        self.edges[start].add(end)
    
    def unset_edge(self, start: A, end: B):
        """Desired: Amortized O(1)"""
        s = self.edges[start]
        s.discard(end)
        if not s:
            self.edges.pop(start, None)

    def connected_endpoints(self, start: Set[A]) -> Set[B]:
        """Desired: Amortized O(len(start) + len(<return>))"""
        empty = set()
        if not start:
            return empty
        return set.union(*(self.edges.get(node, empty) for node in start))
Run Code Online (Sandbox Code Playgroud)

Jea*_*ier -1

正如评论中正确指出的那样,与一组大小相关的边缘对象的len(input)潜在大小len(input) * len(B)意味着读取输入会导致最坏情况的复杂性len(input) * len(B)

因此,没有可能的算法可以保证len(input) + len(result)复杂性。

然而,有多种方法可以通过将二分图表示为 2D 布尔矩阵来获得更快的算法,其中:

  • 每行代表 A 中的一个节点
  • 每列代表 B 中的一个节点
  • 每个单元代表 A 节点和 B 节点之间的连接。

通过这种表示,您可以将行编码为位向量,使用这样的向量,您可以使用单个指令执行多个按位布尔或。(例如,在 64 位架构上,单个按位 OR 将导致 64 个子操作)。

您可以在这里找到更多问题解决方案:

https://cs.stackexchange.com/a/146616

  • 边的数量如此重要的断言是可以证伪的。例如,想象一下,每个设置/取消设置操作都会做额外的工作来预先计算引用该边的 A 的每个可能子集的相关答案。您错过了其他函数的时间界限(和隐式空间界限),但 connect_endpoints 调用将包含正确答案的哈希表查找,从而导致 O(in+out) 运行时间。关于集合并集的链接问题是相关的,但事实上,您在构建图形时能够执行 N^2 工作可能会为这个问题提供更聪明的解决方案。 (2认同)