维护二叉树中的列表顺序

时间:2011-02-16 23:17:00

标签: algorithm binary-tree

给定一系列数字,我想将数字插入到平衡的二叉树中,这样当我在树上进行顺序遍历时,它会给我回序。

如何构造与此要求对应的插入方法?

请记住,树必须平衡,因此没有一个完全无关紧要的解决方案。我试图用AVL树的修改版本来做这件事,但我不确定这是否可以解决。

我也希望能够实现删除操作。删除应删除列表中第i个位置的项目。

编辑:澄清:

我希望: 插入(i,e),它在序列中的第i个元素之前插入单个元素e。 删除(i),删除序列的第i个元素。

如果我插入(0,5),插入(0,4),插入(0,7),那么我存储的序列现在是7,4,5并且在二叉树上的顺序遍历应该给我7, 4,5。

如果我删除(1),那么在二叉树上的顺序遍历应该给我7,5。如你所见,在这种情况下删除第i个序列元素i = 1后维持序列顺序(如我已将我的序列索引为0)。

3 个答案:

答案 0 :(得分:1)

在树中保留一个链接列表。由于树,您已经拥有节点的指针/引用。插入树时,也插入链表的末尾。从树中删除时,从链接列表中删除。因为您有引用,所以它们都是O(1)操作,如果您愿意,可以按插入顺序迭代。

编辑: 澄清Bart的担忧,这是一个Java实现

class LinkedTreeNode<E> {
   E data;
   LinkedTreeNode<E> parent;
   LinkedTreeNode<E> left;
   LinkedTreeNode<E> right;
   LinkedTreeNode<E> insertNext;
   LinkedTreeNode<E> insertPrev;
}

class LinkedTree<E> {
    LinkedTreeNode<E> root;
    LinkedTreeNode<E> insertHead;
    LinkedTreeNode<E> insertTail;

    // skip some stuff
    void add(E e) {
        LinkedTreeNode<E> node = new LinkedTreeNode<E>(e);

        // perform the tree insert using whatever method you like

        // update the list
        node.insertNext = insertTail;
        node.insertPrev = insertTail.insertPrev.insertPrev;
        node.insertPrev.insertNext = node;
        insertTail.insertPrev = node;
    }

    E remove(E e) {
        LinkedTreeNode<E> rem = // whatever method to remove the node
        rem.insertNext.insertPrev = rem.insertPrev;
        rem.insertPrev.insertNext = rem.insertNext;
        return rem.data;
    }
} 

答案 1 :(得分:1)

如果您可以随机访问序列的内容(例如,您有一个数组),那么您可以直接构建树,以便预先遍序遍历提供您想要的序列。它实际上可以在没有随机访问的情况下完成,但效率较低,因为可能需要进行线性扫描才能找到序列的中点。

这里的关键观察是序列中的第一个值位于树的根中,然后序列的其余部分可以分成两半。上半部分将进入以左子项为根的子树,后半部分将进入以右子项为根的子树。因为您在每一步将剩余列表分成两半,所以生成的树将具有log_2 n级并且将是平衡的。

这是一些C ++代码。请注意,“end”是指刚好超过当前数组段末尾的元素的索引。

struct Node {
    Node(int v) : value(v), l(0), r(0) {}

    int value;
    Node* l;
    Node* r;
};

Node* vector_to_tree(int begin, int end, int array[]) {
    if (begin == end)
        return NULL;

    Node* root = new Node(array[begin++]);
    int mid = (begin + end) / 2;
    root->l = vector_to_tree(begin, mid, array);
    root->r = vector_to_tree(mid,   end, array);

    return root;
}

void preorder_traverse(Node* root) {
    if (!root)
        return;

    std::cout << root->value << std::endl;
    traverse(root->l);
    traverse(root->r);
}

答案 2 :(得分:1)

这可以使用AVL树完成。

通过添加到树的最右边节点,将项目添加到AVL树。

由于旋转的性质,AVL平衡不会影响inorder遍历属性。

在每个节点上存储树的大小,并且在插入/删除/余额期间为每个受影响的节点维护这些值可以在O(log n)时间内完成。在每个节点存储树的大小允许按序列中的等级搜索项目,因为树中项目的等级是由节点的左子节点大小+ 1知道的。

从AVL树中删除可以通过用节点右子树的最左边节点替换已删除的节点来完成。

然后在O(log n)时间内完成插入和删除操作,因为树总是平衡的,节点上的大小更新是沿着从插入/删除节点到根节点的路径完成的。

由于除了二叉树之外没有后备数据结构,因此可以完成从树中受益的其他操作,例如在O中查找序列中[m,n]范围内元素的总和(log n )时间。

相关问题