在O(n)时从二叉搜索树中删除多个节点

时间:2012-05-08 15:16:06

标签: algorithm binary-search-tree

考虑二叉搜索树,其中所有键都是唯一的。节点没有父指针 我们有最多n / 2个标记节点 我可以在O(n 2 )时删除所有这些(使用postorder遍历,当遇到标记的节点时,在O(n)处删除)。但这是不合适的。 我需要一个算法来删除 O(n)时间内所有标记的节点。 谢谢。

编辑删除后我需要保持节点顺序不变。
EDIT-2 因此看起来我应该使用典型删除删除每个标记的节点(在左子树中查找最右边的节点并将其与要删除的节点交换)。

3 个答案:

答案 0 :(得分:6)

有许多方法,但这里有一个应该很容易正确的方法,并给你一个完美平衡的树作为副作用。但是,它需要线性额外空间。

  1. 创建一个大小为N的数组减去标记节点的数量(如果您不知道标记了多少节点并且不想检查它,则为N.)
  2. 按顺序遍历,跳过标记的元素,将元素按顺序放置在数组中。这需要线性时间,并且堆栈空间与树的高度成比例。
  3. 使用数组自上而下重建树。数组中的mid元素成为新的根,左子数组的mid元素成为新的左子元素,右子元素的mid元素成为新的右子元素。递归重复。这需要线性时间和对数堆栈空间。
  4. 更新:忘了说跳过标记的元素,但这很明显,对吧? ;)

答案 1 :(得分:2)

我找到了怎么做!

  • 我们使用inorder遍历。
  • 我们检查是否需要在递归调用函数之前删除节点。
  • 当我们找到要删除的节点时,我们将标记为forFindMax并搜索左子树中最右边的节点。
  • 如果我们遇到另一个要删除的节点,我们将所有变量推送到堆栈并在删除节点时弹出它们。
  • 当我们在左子树中找到最大值时,我们会保存对它及其父级的引用 然后,当我们递归地返回到要删除的初始节点时,我们将其删除(以最大值交换它)。
static class LinearDeletion {

    public static Node MIN_VALUE = new Node(Integer.MIN_VALUE);;
    boolean toFindMax = false;
    Node parentOfMax = null;
    Node max = MIN_VALUE;
    Stack<Object> stack = new Stack<>();

    public void perform(Node x, Node parent) {

        if (x.isToDelete) {
            stack.push(toFindMax);
            stack.push(parentOfMax);
            stack.push(max);

            toFindMax = true;
            parentOfMax = null;
            max = MIN_VALUE;

            if (x.left != null) {
                perform(x.left, x);
            }


            if (x.left == null) { //deletion of the node
                if (parent.left == x) {
                    parent.left = x.right;
                } else {
                    parent.right = x.right;
                }
            } else {
                if (x.right == null) {
                    if (parent.left == x) {
                        parent.left = x.left;
                    } else {
                        parent.right = x.left;
                    }
                } else {
                    x.key = max.key;
                    x.isToDelete = max.isToDelete;
                    if (parentOfMax != x) {
                        parentOfMax.right = max.left;
                    } else {
                        x.left = max.left;
                    }
                }
            } // end of deletion
            max = (Node) stack.pop();
            parentOfMax = (Node) stack.pop();
            toFindMax = (boolean) stack.pop();
            if (toFindMax) { // check if the current node is the maximum
                if (x.key > max.key) {
                    max = x;
                    parentOfMax = parent;
                }
            }

            if (x.right != null) {
                perform(x.right, x);
            }

        } else {

            if (x.left != null) {
                perform(x.left, x);
            }

            if (toFindMax) {
                if (x.key > max.key) {
                    max = x;
                    parentOfMax = parent;
                }
            }

            if (x.right != null) {
                perform(x.right, x);
            }
        }
    }
}

答案 2 :(得分:1)

我不明白为什么后序遍历为O(n 2 )。删除单个节点的原因是O(n)是您需要遍历树以查找节点,这是一个O(n)操作。但是一旦找到一个节点,它就可以在O(1)时间内被删除。 * 因此,你可以在O(n)时间内在一次遍历中删除所有O(n)个标记的节点。 / p>

* 除非你需要维护一棵平衡的树。但是,您不能将其列为要求。

编辑正如@njlarsson在评论中正确指出的那样,即使找到节点,删除操作通常也不是O(1)。然而,由于在访问要删除的节点之前遍历左子树和右子树,因此可以在子树遍历期间无需额外成本地获得子树的最小(或最大)元素。这样可以删除O(1)。