【问题标题】:Construct a tree(non-binary) structure from an unknown struct C++从未知结构 C++ 构造树(非二进制)结构
【发布时间】:2013-07-26 08:20:09
【问题描述】:

我需要根据传递给我的结构动态地将子节点添加到树的特定分支。例如,我有这样的结构:

  struct my_struct
  {
  int a;
  int b;
  char c;
  }

在我的函数中,移动到所需的节点后,我应该能够将子节点添加到特定节点,如下所示:

                  root
                   |
      son------daughter----another_son
       |
   a---b--c

我的树节点结构如下:

struct tree{
   string name;
   int child_count;
   int value;
   vector< tree* > child;
};

由于我想稍后更新这些变量中的每一个,我想为结构中的每个变量分离出节点。由于可以在我不知情的情况下更新结构,因此我希望逻辑独立于结构。

【问题讨论】:

  • 您好,实际上我不确定我是否理解您的问题:您是否正在寻找一种将任意结构(具有任意类型的任意成员集)“反汇编”到节点中的方法一棵树?我的意思是:设置一个通用节点类(或结构,如果你愿意的话)不是问题,但是创建函数来“填充”它更复杂,至少你可以认为你的所有结构都是祖先的孩子使用一些“自省启用”方法,如果你不想把代码搞得太乱……
  • 基本上,我希望将不同的结构(或类)写入树的不同分支,以便我可以轻松阅读或更新它们。从上面的例子来看,“女儿”可能会得到一个 {int, float, char} 等结构。拥有一个通用的泛型类看起来很简单,但每次我想更新时,我都会读写额外的变量好。只是想知道这是否可能,否则将使用通用类。

标签: c++ data-structures struct tree


【解决方案1】:

好吧,我可以建议一种方法,而不必太深入细节。我将设置类似于以下代码的内容。基本上有一个通用结构允许某种简化的内省,通过重新定义子结构中的两个纯虚方法,算法将能够将结构“树化”到子节点中。 最后你会发现一个树“导航”的例子。 好吧,这只是一个非常简单的示例,绝不可以认为它是详尽的,您会注意到它没有为更复杂的结构实现任何类型的树递归。无论如何,我不知道选择结构而不是类来处理所有这些的原因。想想吧!

#include <vector>
#include <string>
#include <iostream>

// Allowed data types
enum MyTypes {
    INT,
    DOUBLE,
    NODATA
};

// Base source struct
struct MyBaseStruct {
    std::vector <std::string> ids;
    virtual MyTypes getType(std::string id) = 0;
    virtual void *getPointer(std::string id) = 0;
};

// Example of used struct
struct MyStructA: MyBaseStruct {
    int a;
    double b;
    MyStructA();
    MyTypes getType(std::string id);
    void *getPointer(std::string id);
};

MyStructA::MyStructA()
{
    ids.push_back("a");
    ids.push_back("b");
}

MyTypes MyStructA::getType(std::string id)
{
    if (id == "a")
        return INT;
    else if (id == "b")
        return DOUBLE;
    return NODATA;
}

void *MyStructA::getPointer(std::string id)
{
    if (id == "a")
        return static_cast <void *> (&a);
    else if (id == "b")
        return static_cast <void *> (&b);
    return 0;
}

struct MyTreeNode {
    std::string id;
    MyTypes type;
    void *pointer;
    std::vector <MyTreeNode *> children;
    void addChildren(MyBaseStruct &data);
};

void MyTreeNode::addChildren(MyBaseStruct &data)
{
    std::vector <std::string>::const_iterator i(data.ids.cbegin());
    while (i != data.ids.cend()) {
        MyTreeNode *newNode= new MyTreeNode;
        newNode->id= (*i);
        newNode->type= data.getType(*i);
        newNode->pointer= data.getPointer(*i);
        children.push_back(newNode);
        i++;
    }
}

int main(int /*argc*/, char * /*argv[]*/)
{
    MyStructA a;
    a.a= 1;
    a.b= 12.34;

    MyTreeNode treeRoot;
    treeRoot.id= "root of my tree";
    treeRoot.type= NODATA;
    treeRoot.pointer= 0;
    treeRoot.addChildren(a);

    // Example of tree navigation
    std::vector <MyTreeNode *>::const_iterator i(treeRoot.children.cbegin());
    while (i != treeRoot.children.cend()) {
        std::cout << (*i)->id << ": ";
        if ((*i)->pointer) {
            if ((*i)->type == INT) {
                std::cout << *(static_cast <int *> ((*i)->pointer)) << "\n";
            }
            if ((*i)->type == DOUBLE) {
                std::cout << *(static_cast <double *> ((*i)->pointer)) << "\n";
            }
        }
        i++;
    }
}

如果您有时间,您还可以考虑编写一种变体类来处理不同类型的数据类型,并避免需要在多个地方强制转换您的值。

【讨论】:

  • 没有硬性规定我需要使用结构。我会尝试使用某种变体类。
【解决方案2】:
struct tree{
   string name;
   my_struct structure;
   int sonc;
   vector< tree* > child;
};

我建议您使用 Node 作为树节点的结构名称(那些可以组装树的节点) 并使用诸如“父”和“子”之类的术语来表示树中不同深度的节点。

顺便说一句,如果你想向上遍历树,我建议添加另一个指向节点父节点的指针。

然后编写一个函数,在其中你有一个节点(树)作为你添加子节点的参数......

像这样:

addToTree(tree node, other parameters, like struct...)
{
    tree newNode = new tree();
    //do what you want with the new node...

    node.children.add(newNode);
    node.sonc++;
    newNode.Parent = node;
}

【讨论】:

  • 这个解决方案的问题在于我不能为不同的节点有不同的结构。基本上,我想根据结构中变量的数量来创建子节点,不同的分支结构会有所不同。
  • 啊,这就是你在结构中所做的更改:) 你总是可以使用 void* 指针在每个节点中存储不同的类型。然后,我还会推荐一个标记,您可以在其中存储数据类型...例如,char 类型和 'c' 用于 char 'i' 用于 int,'d' 用于 double 和一些您自己的值用于不同的结构类型。
【解决方案3】:

使用数据类型

struct data {
   string name;
   my_struct structure;
   int sonc;
};

然后定义你的树一般地


现在我建议使用 Boost Containers,这样您就可以创建一个不完整类型的向量/列表/去队列:

template <typename Data>
struct node
{
    Data _data;
    boost::deque<node> _children;
};

或者,如果您喜欢继承,这也可能是有效的:

template <typename Data>
struct node : public Data
{
    boost::deque<node> _children;
};

这让您免于手动分配管理的所有麻烦。

或者,您可以使用 std::unique_ptr&lt;node&gt; 来避免手动进行内存管理(提示:很难做到正确。想想异常安全、自分配、循环)

甚至,使用 boost::variant:

typedef boost::make_recursive_variant<data, std::vector<boost::recursive_variant_> >::type tree;

最能召集你的将取决于应用程序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-30
    • 2023-03-28
    • 1970-01-01
    • 2014-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多