【问题标题】:Dynamic tree in C++C++中的动态树
【发布时间】:2012-02-26 10:27:02
【问题描述】:

我想制作一棵树,它可以在每个节点中有一些子节点,但我不知道它们的数量。树必须在小内存中使用(没有额外数据)以恒定时间到每个节点进行编码。我坚信我将创建具有值和子属性(值是 int,子项是堆栈)和指向该树中每个节点的指针数组的类树。我的问题是制作这个数组。我怎样才能在没有额外数据的情况下(std::vector 有时分配的内存比它需要的更多)和每个单元格的恒定时间?

一切正常,但我还需要固定时间到每个节点。我知道会有多少个节点,但我不知道如何制作每个节点的数组。它应该像这样工作: array[n];
A_Node *array[0]= new A_Node(16);
A_Node *n = new A_Node(1);
array[0]->addChild(n);
array[1]=n;
要么: *(数组+1)=n;

【问题讨论】:

  • 您需要能够对该树执行哪些操作?
  • 添加节点,将子节点添加到节点,添加每个节点后,我将创建数组以平整这棵树(但不是直的)。
  • “孩子是堆栈”是什么意思?
  • std::vector 分配更多内存,因为它希望为您节省多次对 malloc 的不确定性昂贵的调用(当您一次增长一个项目时)。如果你知道你的向量应该有一个固定的大小,你可以使用.reserve(size),并且你的树永远不会有比你在那里定义的更多的节点。
  • @Frg:您不想使用传递容量的构造函数吗?此外,reserve 确保向量至少具有指定的容量。再说一次,我不确定是否允许构造函数版本过度分配。

标签: c++ arrays pointers tree


【解决方案1】:

这是一个可能的例子。这不是一个完整的示例解决方案,但我希望你明白这一点。关键是你可以有一个指向节点的双指针,它基本上是一个指向树节点的指针数组。

然后,您可以自己重新分配大小,并在需要时根据需要重新分配大小。但是 std::vector 已经为您做到了,因此没有真正的理由不使用它,除非您想自己控制一切或进行实验,或者正在用 C 编写一些东西。无论如何希望这会有所帮助。

#include <stdio.h>
#include <stdlib.h>

// The initial buffer length of a node's children
#define BUFFER_LENGTH   5
// How much to multiply with if an addition of a child goes over the buffer
#define MULTIPLIER 2

///Your node class
class A_Node
{
    public:
    A_Node(int value,unsigned int childrenN=0)
    {
        this->value = value;
        this->childrenN = childrenN;

        //allocate BUFFER_LENGTH children for the node at first or childrenN if the childrenN is not initially 0
        if(childrenN != 0)
        {
            this->children = (A_Node**) malloc(sizeof(A_Node*)*childrenN);
            this->bufferLength = childrenN;
        }
        else
        {
            this->children = (A_Node**) malloc(sizeof(A_Node*)*BUFFER_LENGTH);
                        this->bufferLength =BUFFER_LENGTH;
        }
    }

    //in the destructor of a node it would need some special care
    ~A_Node()
    {
        //for every child call the destructor of each child
        for(int i = 0; i < this->childrenN; i++)
        {
            delete this->children[i];
        }

        //and only then free the buffer of the pointers to the children
        free(this->children);
    }

    //adds a child
    void addChild(A_Node* child)
    {
        //reallocate if needed
        if(childrenN >= this->bufferLength)
        {
            realloc(this->children,sizeof(A_Node*)*MULTIPLIER);
        }

        this->children[childrenN] = child;
        this->childrenN++;
    }

    A_Node* getChild(unsigned int i)
    {
        if(i >= this->childrenN)
        {
            return 0;
        }

        return this->children[i];
    }

    void printValue()
    {
        printf("%d\n",this->value);
    }
private:
    int value;
    unsigned int childrenN;
    A_Node** children;
    unsigned int bufferLength;
};

///Your tree class
class A_Tree
{
    public:
        //constructor
        A_Tree(int rootValue)
        {
            root = new A_Node(rootValue);
        }
        //destructor
        ~A_Tree()
        {
            //recursively kills all the nodes due to the destructor of node
            delete root;
        }
        //your root node
        A_Node* root;
};


int main()
{
    A_Tree tree(16);

    tree.root->addChild(new A_Node(42));

    tree.root->printValue();

    (tree.root->getChild(0))->printValue();


    return 0;
}

【讨论】:

  • 啊,我忘记了我的 dtor。傻我。 +1 :)
  • 一切都好,但我也需要固定时间到每个节点。我知道会有多少个节点,但我不知道如何制作每个节点的数组。它应该像这样工作: array[n];<br> A_Node *array[0]= new A_Node(16);<br> A_Node *n = new A_Node(1);<br> array[0] -&gt;addChild(n);<br> array[1]=n;<br> 或者: *(array+1)=n;
  • @PiotrŁużecki 我在你的问题中看到了编辑,我不太明白你的意思。您确实意识到 A_Node *array[0]= new A_Node(16);编译不对?如果您只是意味着您事先知道每个节点有多少个子节点,那么我的解决方案也涵盖了这一点。您所要做的就是将 childrenN 参数提供给 A_Node ctor
  • 我知道它不能编译:/你知道这个熟悉的解决方案吗?我知道树中有多少个节点,但现在每个节点将有多少个子节点,所以你的解决方案很棒。但是当我从输入中读取数据时,我必须将新节点放在随机节点中,所以我需要每个节点的恒定时间(这棵树可能很大)。
  • @PiotrŁużecki 我还是不明白你的意思。你能提供一个输入数据的样本以及你想用它们做什么吗?还提供您想要做的某种形式的伪代码解决方案。这样,人们将能够帮助你并且更倾向于帮助你,因为你会付出一些努力来写一个更有意义的问题。但是因为这听起来像是一个不同的问题,我相信你必须将它作为一个不同的问题来问。注意:添加输入数据示例、逻辑伪代码和问题。
【解决方案2】:

只需自己跟踪内存,而不是使用向量:

class Node {
public:
    // In the constructor, initialize your array of children to NULL
    // and the size of your children array to zero
    Node() : mChildren(NULL), mSize(0) {}

    void AddChild(Node* newChild) {
        // allocate space for your new array
        Node** newArray = new Node*[mSize + 1];

        // copy over nodes from old array to new array
        for (int i = 0; i < mSize; i++) {
            newArray[i] = mChildren[i];
        }

        // add in our new child to the end of the array
        newArray[mSize++] = newChild;

        // if there was an old array (null check) free the memory
        if (mChildren) {
            delete [] mChildren;
        }

        // set our children array equal to our new array
        mChildren = newArray;
    }

    Node* AccessChild(size_t index) {
        // make sure it's a valid index and then return
        assert(index < mSize);
        return mChildren[index];
    }

private:
    Node** mChildren;
    int mSize;
};

这将没有额外的空间用于额外的节点,但它需要一个 int 的大小,以便跟踪您存储的节点数量。如果没有这个或有固定数量的孩子,我看不出有什么办法可以做到。

请注意,每次需要重新分配向量时,它们的大小都会翻倍,因为这样更有效。虽然上述解决方案在内存方面会更有效,但在性能方面会受到很大影响,因为它需要为每个添加子节点进行分配,这将需要 O(N) 分配来添加 N 个节点。

向量的性能将是 O(log(N)) 分配来添加 N 个节点,但同样,这个解决方案听起来像它具有您正在寻找的内存效率。

【讨论】:

  • 一切都好,但我也需要固定时间到每个节点。我知道会有多少个节点,但我不知道如何制作每个节点的数组。它应该像这样工作: array[n];<br> A_Node *array[0]= new A_Node(16);<br> A_Node *n = new A_Node(1);<br> array[0] -&gt;addChild(n);<br> array[1]=n;<br> 或者: *(array+1)=n;
  • 请在您的问题的编辑中发布代码,而不是将 cmets 复制到每个答案。它更具可读性。此外,如果您的问题没有被回答,如果它没有被标记为已回答,它会得到更多的关注。如果您有一个与第一个问题无关的后续问题,请考虑完全提出一个新问题。
猜你喜欢
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
  • 2012-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-22
相关资源
最近更新 更多