如何从文件重建BST

时间:2010-02-11 03:53:35

标签: c++ file indexing linked-list binary-tree

我的C ++程序从用户输入创建不平衡的BST,并将其保存到磁盘。它通过首先通过执行预订遍历并为每个节点分配唯一编号来索引每个节点来实现此目的。接下来,它将BST输出到文件。它通过执行预先遍历遍历,然后为每个节点打印来存档数据值,左子节点的索引号和右子节点的索引号。

因此,在将BST保存到磁盘后,内存中的BST将被破坏。我想在文件中读取并重新创建BST,以便它与以前完全一样。

5 个答案:

答案 0 :(得分:1)

将所有节点与左右子索引(例如散列表)一起读入内存。然后从root(它是文件中的第一个节点)开始,并通过哈希表中的索引查找左右子项。以DFS或BFS方式对子节点重复相同的过程。要么应该工作。

您可以对此进行优化,以便在构造树之前将整个数据加载到内存中。您可以读取节点并以DFS方式构造树。因此,添加左孩子是直截了当的。在添加正确的孩子时,您必须检查索引号。如果不匹配,请尝试在DFS排序中将该子节点添加到高于当前节点的节点中。

答案 1 :(得分:1)

后序更简单。您可以将已还原的节点推送到堆栈,并且要链接到父节点的子节点始终是堆栈的最顶层条目。

您只需要一次传递,只要您记录每个节点保存了哪些子节点,这样您就知道要从堆栈中弹出什么以及要分配哪些子节点。

答案 2 :(得分:1)

让我们假设您事先知道树(N)的大小。也许它是文件中的第一行。如果不是,则很容易调整它以动态地重新调整索引向量的大小。请注意,这是半伪代码:

// Only needed while parsing the file
std::vector<Node*> index(N, NULL);

// We can always create the root node.
// This simplifies the while loop below.
index[0] = createNode(0);

while (!in.eof()) {
    int nodeID = -1, leftID = -1, rightID = -1;
    parseNode(in, &nodeID, &leftID, &rightID);

    // Guaranteed to be non-NULL
    Node* node = index[nodeID];

    // if leftID or rightID is -1, createNode()
    // will simply return NULL.
    index[leftID] = createNode(leftID);
    index[rightID] = createNode(rightID);

    node->setLeftChild(index[leftID]);
    node->setRightChild(index[rightID]);
}

node = index[nodeID]保证为非NULL的原因是因为预订索引/写入/读取。我们在开始时预先创建了根节点,所有其他节点由父节点创建为左或右子节点。

编辑:我刚刚意识到我们不需要完整的主索引。我们只需要存储潜在的右子节点以在堆栈中扩展。这是该算法的版本:

// Candidate right-child nodes to expand
std::stack<Node*> rightNodes;

// Pre-create the root node as "left child"
Node* left = createNode(0);

while (!in.eof()) {
    // We already know the next node. It is the previous
    // node's left child (or root), or the nearest
    // parent's right child.
    Node* node;

    if (left != NULL) {
        node = left;
    }
    else {
        node = rightNodes.top();
        rightNodes.pop();
    }

    parseLine(in, &nodeID, &leftID, &rightID);
    assert(node->ID() == nodeID);

    // if leftID or rightID is -1, createNode()
    // will simply return NULL.
    left = createNode(leftID);
    Node* right = createNode(rightID);

    node->setLeftChild(left);
    node->setRigthChild(right);

    if (right != NULL) {
        rightNodes.push(right);
    }
}

答案 3 :(得分:1)

如果您将二进制搜索树存储为预订遍历,那么只需在读取它们时一次插入一个元素即可获得的树将与您开始时的树相同。那当然需要O(n log n)时间。如果您愿意存储外部节点(Null)那么您可以执行以下操作:

ReadBSTPreOrder(node ** target) {

    node * n = readNode();
    *target = n;

    if (n == NULL) return;
    ReadBSTPreOrder(&node->left);
    ReadBSTPreOrder(&node->right);
}

这样做的另一个好处是可以处理非BST的二叉树。 如果您愿意使用表示法来存储Null,则可以是单个位,但是对于空值而言,单个字节标记和用于记录的不同标记应该可以。这也将使你免于写出索引。

答案 4 :(得分:0)

假设你没有达到最佳范围,你可以使用这个简单的算法。

nodes_list = []
For each line i:
    Search for node i in nodes_list
    if(found):
        node_i = found_node
        node_i.set_value(line_value)
    else:
        node_i = new node(i)
        node_i.set_value(line_value)
        nodes_list.add(node_i)
    Search for line_left_node in nodes_list
    if(found):
        node_i.set_left_node(found_node)
    else:
        left_node = new node(line_left_node)
        node_i.set_left_node(left_node)
        nodes_list.add(left_node)
    Search for line_right_node in nodes_list
    if(found):
        node_i.set_right_node(found_node)
    else:
        right_node = new node(line_right_node)
        node_i.set_right_node(right_node)
        nodes_list.add(right_node)