如何存储Euler图结构?

时间:2014-06-13 02:50:45

标签: algorithm graph graph-algorithm

我正在解决Euler Path问题,并发现了一个问题:如何定义或存储Euler图结构?

通常的方法是使用"伴随矩阵",C [i] [j]被定义为存储ij之间的边缘。它简洁有效!但这种矩阵是受限于2个节点之间的边缘是唯一的情况(图1)。

class EulerPath
{
   int[][] c;//adjoint matrix,c[i][j] means the edge between i and j
}

如果有多个边缘怎么办(图2)?我的解决方案可能是使用自定义类,如" Graph"," Node"," Edge"存储图形,但将图形划分为一些离散的结构,这意味着我们必须考虑更多的类细节,可能会损害效率和简洁性。因此,我非常渴望听到您的建议!非常感谢!

class EulerPath
{
   class Graph
   {
      Node[] Nodes;
      Edge[] Edges;
   }
   class Node{...}
   class Edge{...}
}

enter image description here enter image description here

4 个答案:

答案 0 :(得分:2)

您可以使用邻接矩阵来存储具有多边的图形。您只需让c[i][j]的值为顶点i与顶点j相邻的次数。在你的第一种情况下,它是1,在你的第二种情况下,它是3.参见Wikipedia - 邻接矩阵没有被定义为仅由1和0组成,这只是一个简单图形的邻接矩阵的特例。

编辑:您可以在邻接矩阵中表示您的第二个图形,如下所示:

  1 2 3 4
1 0 3 1 1
2 3 0 1 1
3 1 1 0 0
4 1 1 0 0

答案 1 :(得分:1)

您至少可以通过以下三种方式执行此操作:

邻接列表

意味着您有一个名为al [N] [N]

的2D数组

al [ N ] [N] N 是节点索引

al [N] [ N ] N 是邻居节点索引

示例,带有此输入的图表:

0 => 1
1 => 2
2 => 3
3 => 1

邻接列表如下所示:

0 [1]
1 [2,3]
2 [1,3]
3 [1,2]

PS :由于这是一个2D数组,并且不会使用所有水平单元格,因此您需要跟踪每个图形索引的连接邻居数量,因为某些编程语言已初始化数组值为零,这是图中的节点索引。这可以通过创建另一个数组来轻松完成,该数组将计算每个图索引的邻居数。此案例示例:numLinks: [1,2,2,2]

<强>矩阵

使用矩阵,您可以创建一个N x N 2D数组,并在行col neighobor节点的交集中放置一个1值:

上面输入相同的示例:

  0 1 2 3
0 0 1 0 0
1 1 0 1 1
2 0 1 0 1
3 0 1 1 0

班级节点

最后一种方法是创建一个名为Node的类,它包含一个Node类型的动态数组。并且您可以在此阵列中存储其他连接的节点

答案 2 :(得分:0)

考虑使用链表向量。添加一个包含Vertex字段的字段以及Weight(我们将其命名为Entry)。您的权重最好是另一个向量或链表(最好是ll),它将包含所有可能的权重到Vertex。你的主类将有一个向量向量,或链接列表向量(我更喜欢链表,因为你很可能不需要随机访问,在执行任何操作时被迫迭代每个Entry。您的主类将有一个包含所有顶点的向量。在C ++中,这看起来像这样:

class Graph{
    std::vector<std::forward_list<Entry>> adj_list;
    std::vector<Vertex> vertices;
};

Vertex对应的vertices[i]adj_list[i]中具有相应列表的位置。由于每个Entry都包含有关您所关联的Vertex的信息以及相应的权重,因此您将使用此类代表您的图表。

答案 3 :(得分:0)

什么类型的操作的效率?

如果要在互联网上找到两个IP地址之间的路由,那么您的邻接矩阵可能是一百万个节点的平方,即一千兆字节的条目。并且当找到连接到给定节点的所有节点上升为n时,您可以查看每个节点的一百万个查找,以找到连接到该节点的节点。非常低效。

如果你的问题只涉及几个节点并且不经常运行,那么邻接矩阵就很简单直观。

对于涉及遍历图形的大多数问题,更好的解决方案可能是创建一个名为node的类,该类具有一个属性集合(例如List),它包含它所连接的所有节点。对于大多数实际应用程序,连接节点列表远小于所有节点的总数,因此这更加紧凑。此外,它在查找边缘方面非常高效 - 您可以在每个节点的固定时间内获得所有连接节点的列表。

如果你使用这个结构,你有一个节点类,它包含它所连接的所有节点的集合作为属性,那么当你创建一个新边(比如节点A和节点B之间)然后你添加B到A连接的节点集合,A到B连接的节点集合。请原谅我的Java / C#,比如

class Node{
Arraylist<Node> connectedNodes;

public Node()  // initializer
{
connectedNodes = new ArrayList<Node>;
}
}

// and somewhere else you have this definition:
public addEdgeBetween(Node firstNode, Node secondNode) {
firstNode.connectedNodes.Add(secondNode);
secondNode.connectedNodes.Add(firstNode);
}

类似于删除边缘,删除A到B集合中的引用,反之亦然。不需要定义单独的边类,在交叉链接两个节点的结构中隐含边。

关于实现这种结构所需要做的就是(对于大多数现实世界的问题)使用的内存远远少于邻接矩阵,对于大多数问题,大多数节点要快得多,并且最终更加灵活。

定义节点类还会打开一个逻辑位置来添加许多种类的增强功能。例如,您可能决定为每个节点生成两步之外的所有节点的列表,因为这样可以改进路径查找。您可以轻松地将其添加为节点类中的另一个集合;这对于邻接矩阵来说是一件相当混乱的事情。显然,您可以将更多功能集中到一个类中,而不是压缩到一个整数矩阵中。

关于多重链接的问题我不清楚。如果你想在相同的两个点之间有多个边,那么这可以通过两种方式来实现。在邻接矩阵中,只需在该行和列上有一个数字,表示链接数。如果使用节点类,则只需单独添加每个边。类似的方向图;从A到B的边缘在A的连接节点列表中引用B,但B在其列表中没有A.