C ++树数据结构

时间:2018-06-30 01:53:26

标签: c++ tree

背景

因此,我一直在将一些较旧的Java代码移植到C ++,并且遇到了使执行过程变得非常困难的问题。我的项目使用树形数据结构来表示3D动画的节点层次结构。

Java:

:

在Java中,创建允许修改等的树非常简单。

问题:

我遇到了C ++的问题,如果不手动分配新的内存块并移走现有的内存块就无法轻松地添加数组,所以我切换到public final class Node { private final Node mParent; private final ArrayList<Node> mChildren; //private other data, add/remove children / parents, etc ... } 。向量有做我刚才描述的事情的问题,使指向那里元素的任何指针无效。因此,基本上,如果您不想使用指针,则需要一种方法来备份它们,以便保存实际节点的内存不会移动。我认为您可以使用std::vector / std::shared_ptr将节点包装在std::unique_ptr中,我尝试使用这种方法,但是变得很笨拙。另一个选择是有一个“树”类来包装节点类,并且是操作它的接口,但是(对于我的用例而言)处理切断分支并使它们成为自己的树会很烦人。并可能附有不同的分支。

我在网上看到的大多数示例是具有2个节点的二叉树,而不是动态的,或者它们对内存泄漏等有很多评论。我希望上面的Java代码有一个不错的C ++替代方法(不存在内存泄漏)问题等)。而且我也不会进行任何排序,树的目的是维护层次结构而不是对其进行排序。

老实说,我真的不确定要走什么方向,我在过去的两天里一直在尝试不同的方法,但是这些方法都不“正确”,而且通常管理起来都很尴尬,任何帮助都将不胜感激!< / p>

编辑:

关于shared_ptrs为何笨拙的修改:

std::vector

使用上面的指针变得非常冗长,我希望有一个更简单的解决方案。

3 个答案:

答案 0 :(得分:2)

您看到的差异的唯一原因是,如果将对象直接放在c ++中的向量本身中(在Java中不能这样做)。然后将它们的地址绑定到向量中当前分配的缓冲区。区别在于Java中,所有对象本身都是分配的,因此数组中实际上只有一个“对象引用”。在c ++中,等效的方法是创建一个指针向量(希望包装在智能指针对象中),因此向量元素只是一个地址,但对象位于固定内存中。它增加了一个额外的指针跳,但其行为将更像您在Java中所期望的。

struct X {
   char buf[30];
};
std::vector<X> myVec{ X() };

鉴于上述情况,myVec中的X元素在分配中是连续的。 sizeof(myVec [0])== sizeof(X)。但是,如果您将指针放在向量中:

std::vector<unique_ptr<X>> myVec2{ make_unique<X>() };

这应该表现得更像您想要的,并且当矢量调整大小时指针不会变得无效。指针将仅被复制。

您可以执行此操作的另一种方法是在设计中进行一些更改。完全考虑一下指针的替代方法,其中树包含元素向量,而节点包含整数向量,这些向量是该向量的索引。

答案 1 :(得分:1)

vectorforward_list,...,可以使用任何std容器类(内置数组或std :: array除外)。 您的麻烦似乎是Java类是引用类型,而C ++类是值类型。下面的代码段在编译时触发“无限递归”或“使用不完整类型”错误:

class node{
    node mParent;//trouble
    std::vector<node> children;
    //...
};

mParent成员必须是引用类型。为了强加引用语义,您可以使其成为原始指针:

node* mParent;

您也可以将指针用作容器的参数类型,但作为C ++入门者,很可能会导致内存泄漏和运行时错误。我们现在应该尝试远离手动内存管理。因此,我将您的代码段修改为:

class node{
private:
    node*  const mParent;
    std::vector<node> children;
public:
    //node(node const&)=delete;//do you need copies of nodes? you have to properly define this if yes.
    node(node *parent):
        mParent{parent}{};
    void addChild(/*???*/){
         children.emplace_back(this);
        //...
    };
    //...
};

答案 2 :(得分:1)

您可以将节点存储在单个向量中,并使用调整向量大小时不会改变的相对指针:

typedef int32_t Offset;
struct Node {
    Node(Offset p) : parent(p) {}
    Offset parent = 0; // 0 means no parent, so root node
    std::vector<Offset> children;
};

std::vector<Node> tree;
std::vector<uint32_t> free_list;

要添加节点:

uint32_t index;
if (free_list.empty()) {
    index = tree.size();
    tree.emplace_back(parent_index - tree.size());
} else {
    index = free_list.back();
    free_list.pop_back();
    tree[index].parent = parent_index - index;
}

tree[parent_index].children.push_back(index - parent_index);

要删除节点:

assert(node.children.empty());
if (node.parent) {
    Node* parent = &node + node.parent;
    auto victim = find(parent->children.begin(), parent->children.end(), -node.parent);
    swap(*victim, parent->children.back()); // more efficient than erase from middle
    parent->children.pop_back();
}
free_list.push_back(&node - tree.data());