【问题标题】:Parse text file to C++ directed graph or adjacency list? [closed]将文本文件解析为 C++ 有向图或邻接表? [关闭]
【发布时间】:2015-11-03 20:37:51
【问题描述】:

我有一个类似于以下内容的文本文件:

person: head, body
head: eyes, nose, ears, mouth
body: arm, leg
arm: elbow, hand
leg: thigh, knee, foot

我试图在邻接表或有向图中表示这一点。最好的方法是什么?我不知道最好的数据结构或如何在 C++ 中表示它。

我尝试过使用带有键值的结构(人、头等),它是父索引,它是作为向量的子元素:

struct Node
{
    string key;
    int parentIndex;
    vector<string> children;
};

但这似乎效率不高。有什么想法吗?

也许这样会更好?

struct {
    string key;
    Node* parent;
    vector<Node> children;
};

【问题讨论】:

  • @Christophe 我编辑了这个问题。
  • 要读取文件,您应该使用std::ifstream。对于解析行,不要忘记 C++ 是基于 C 构建的 - 查看 C 的 strtok 函数。对于存储,请考虑std::map&lt;[key], [container]&lt;[key]&gt;&gt; 的一些风格。
  • @Conduit 是说我们应该使用 map>?为了查询这个结构,我们想使用类似图的算法,如 DFS 等。
  • 您的结构实际上是一棵树:每个节点都有一个父节点,并且可能有几个子节点。这是你打算做的吗?另外,我是否理解您的节点本身存储在向量中(当您按索引而不是名称搜索时)?
  • 但它仍然是一棵树:您的节点只有一个父节点,并且子节点包含在父节点中。如果您的数据结构确实是一棵树,那么您的编辑就可以了。但是,如果您可以有断开的零件,或者如果一个零件可能是多个零件的“孩子”,那么您可能会遇到问题。

标签: c++ data-structures graph


【解决方案1】:

您的数据样本有几个问题仍未得到解答:是否总是有一个单一的入口点(例如:人)?它总是自上而下的分解(即每个元素最多有一个父元素)吗?元素是否总是以正确的顺序出现:首先是顶部,然后是向下?

如果所有三个问题的答案都是肯定的,那么你提出的结构是合适的:

  • 如果总是从顶部开始探索,它就足够高效了。
  • 查找特定节点会很耗时,因为您必须遍历整个结构。

还有几点需要修复:

  • 复制节点会很棘手(因为指向父节点的指针必须为复制的节点及其子节点和子节点的子节点更改。
  • 向数组中添加元素可能会使所有子项和子项的子项的父指针无效。

如您所见,最佳数据结构不仅取决于内容,还取决于您将如何使用它。

还有很多其他方法可以做到这一点,以不同的方式平衡性能方面。例如:

class mygraph {
    struct node {  // nodes that you read:  
       string name;  
       int id;            // index of the node in the nodes vector
       vector<int> in;    // parent(s) that can lead to this node
       vector<int> out;   // children you can go to
    };
    vector<node> nodes;    // all the nodes in arbitrary sequential order  
    map<string, int> dict; // map converting the names into ids (redundant and optional, useful for efficien search by name); 
public: 
    // members to populate the structure and to acces the nodes cleanly.   
};

优点:

  • 通过 id 查找任何节点非常快,因为它只是索引一个数组。
  • 您无需担心结构副本,因为没有指针。
  • 由于输入/输出向量,您可以从所需的任何节点快速向前或向后移动。
  • 冗余映射(即索引和名称)加速按名称搜索节点

不便之处:填充结构时存在一些开销:您需要通过验证地图中的名称将每个名称转换为 id,如果不存在,则在节点向量中创建一个新节点并在地图中插入名称+新 id。

【讨论】:

    【解决方案2】:

    每个“词”都需要自己的节点。所以,你的第二个结构更接近。但是,只要确保子节点的容器类型可以在创建节点后动态增长并包含指向子节点的指针。

    有两个原因:
    (1) 假设我们在您的数据底部添加了一行:arm: wrist。它需要在创建 arm 节点之后附加到子节点。
    (2) 目前,您的数据文件有序。但是,如果我们随机化您的数据线的顺序会怎样?那还能用吗?大多数有向图程序都能处理得很好

    当你从左到右解析一行时,得到这个词。搜索所有已知节点,寻找匹配项。解析完第一行后,您就有了三个节点:person、head、body。 head 和 body 在 person 的子项中 [并且他们的 parent 字段都将指向 person],但他们的子列表 [当前] 为空。 person 将是 root 节点。

    如果您找到匹配项,您将在第二行的 head 中找到匹配项,请使用现有的 head 节点,并填写子节点。如果没有匹配,则创建节点。

    如果搜索失败并且您必须创建一个新节点,则您正在构建的树中可能没有位置可以将其附加到。在这种情况下,将其添加到“保留”列表[这也是搜索的一部分],直到有更多节点进入并且您可以将保留项作为子项附加,在这种情况下,您可以将它们从保留列表中删除。

    您可能需要更新root 节点。对于样本数据,假设我们在底部添加了household: person dogroot 节点需要相应调整。我们还需要一个dog: 行。如果我们最后没有它,那是一个 undefined 值并且需要被标记。您的节点定义需要 defined bool 才能标记。

    最后,保留列表中的任何内容都是无关数据。例如,如果我们有一个 zebra: 行,则没有对它的引用[并且它的 parent 将为空],这也需要被标记。

    当链接事物时,一个给定的节点只能被引用一次。因此,如果我们添加household: arm,这将发生冲突,因为 arm 已经是 body 的子级。通过parent 已经非空来检测这个

    【讨论】:

      猜你喜欢
      • 2013-10-28
      • 1970-01-01
      • 1970-01-01
      • 2013-03-19
      • 2011-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-06
      相关资源
      最近更新 更多