C++ 图是如何定义的?

C++ 图是如何定义的?,c++,algorithm,graph,C++,Algorithm,Graph,我看到一个面试问题,其图表结构定义为: struct Node{ vector<Node*> neighbors; } struct节点{ 向量邻域; } 我认为这是不寻常的,或者可能是一个错误,因为在节点之间没有真正的区别。我的推理正确吗?或者一个图可以用一个邻域向量来正确定义吗?我想我们应该有这样的东西: template<typename T> struct Node{ T value; vector<Node*> neighbor

我看到一个面试问题,其图表结构定义为:

struct Node{
   vector<Node*> neighbors;
}
struct节点{
向量邻域;
}
我认为这是不寻常的,或者可能是一个错误,因为在节点之间没有真正的区别。我的推理正确吗?或者一个图可以用一个邻域向量来正确定义吗?我想我们应该有这样的东西:

template<typename T>
struct Node{
   T value;
   vector<Node*> neighbors;
}
模板
结构节点{
T值;
向量邻域;
}
这对我来说更直观


有什么“典型”的方法来定义图形吗?例如,对于二叉树,我们(至少)有一个值,加上左指针和右指针。对于链表,我们(至少)会有一个值和一个下一个指针,等等。

这是表示图形的非常正常的方式,特别是在面向对象语言中。它的可扩展性也很强,可以表示数百万个相互连接的节点。您还可以使用它来表示图形,这有助于学习算法和实现示例代码。但这并不是表示数百万个节点的好方法

在您提供的两个数据结构之间,我认为第二个更实用,因为您总是希望在每个节点中存储一些值。但在某些情况下,您没有任何存储,在这种情况下,您也可以很好地使用第一个数据结构。例如,解决最大流量问题

所以答案应该是“基于你试图解决的问题”

使用邻接矩阵解决的任何问题都可以用提供的第一个数据结构表示。不只是需要顶点和边的数据结构通常属于更高级的“增强数据结构”,这是解决大多数实际问题所必需的

更多参考资料:


  • 你说得对。如果没有与图中每个顶点(节点)相关联的任何标识信息,则无法对图进行太多操作


    面试官可能会漏掉它,或者他可以用这样的想法来证明它的合理性,即他可以维护一个地图,将每个节点与一个id或名称关联起来。当信息量大或维护成本高时,他们会这样做。也就是说,指向每个节点的数据的指针比基于地图的方法更常见。

    如果它是一个小图形,可以用邻接矩阵表示它。
    每个单元格(i,j)表示该节点与j相邻。

    您希望节点具有哪些属性?我希望看到一组可变的属性。对于某些算法,任何固定的节点(或边)属性集都可能不够。这促使我开发了我称之为“数据访问器”和中称之为“属性映射”的东西。使用节点地址来确定图的属性不会让我有丝毫的不安,尽管我希望在大多数情况下使用索引之类的东西。根据图形大小是已知的还是动态创建的,使用
    std::unordered_map
    确定属性值可能更有效。此外,根据算法,仅可访问图的部分

    图形可以用不同的方式表示。示例中的邻接列表就是一个示例。大多数情况下,您最好使用关联列表(即,您有明确的边表示,例如,如果您有具有不同属性的平行边,这一点很重要)。如果没有平行边且图形几乎完成,则可能有一个邻接矩阵。使用适当的抽象来运行算法,您甚至可以隐式表示图形。例如,您可以定义一个图,其中索引为N和M的节点相邻,如果N和M具有GCD或某个值


    属性的表示方式主要取决于算法的需要。给定的节点类型看起来似乎是用于面向对象的设置中,但不太可能产生良好的性能。考虑到许多有趣的图形算法具有非线性复杂性,快速操作会产生不同。

    在高性能计算文献中,图形通常使用压缩稀疏行(CSR)格式表示,该格式也适用于稀疏矩阵计算。许多真实世界的图形(路线图、社交网络等)都是稀疏的,这使得邻接矩阵在空间上非常浪费。对于稀疏图,CSR格式在性能方面优于邻接列表,因为它需要较少的内存回迁

    我为NVIDIA的Parallel Forall博客写了一篇文章,其中包括一幅图形的示例图片及其这种格式的表示。请参阅“GPU上的稀疏图表示”一节。事实证明,许多基于CPU的HPC图算法也使用这种格式

    要总结这篇文章,请看下图:


    (来源:)

    (忽略
    BC[x]=y
    标签)

    如果我们将顶点从0…8而不是1…9重新编号为索引,则可以按CSR格式表示图形:


    (来源:)

    其中,
    R
    是行偏移量数组,
    C
    是列索引数组。行偏移数组是一个n+1元素数组,它指向列索引数组中每个顶点邻接的开始和结束位置。例如,顶点
    u
    的邻接列表位于
    C[R[u]
    C[R[u+1]-1]
    之间


    因此,对于上面的例子,如果我们看一下图中的顶点4,我们将其重新编号为顶点3,我们可以看到
    R[3]=8
    R[4]=12
    ,这意味着与3相邻的顶点位于
    C[8]
    C[12]
    ,或者是顶点{0,2,4,5},对应于{1,3,5,6}如图所示。

    哪种方式正常?只是邻居的向量?还是包含值的那个?@BobJohn深度复制问题正在讨论中