繁体   English   中英

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

[英]Delete multiple nodes from a binary search tree at O(n) time

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

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

有很多方法,但是这是应该很容易解决的一种方法,它可以为您提供完美平衡的树作为副作用。 但是,它需要线性的额外空间。

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

更新:忘了说跳过标记的元素,但这很明显,对吧? ;)

我已经找到了怎么做!

  • 我们使用有序遍历。
  • 我们在递归调用函数之前检查是否需要删除节点。
  • 当我们找到要删除的节点时,我们将标志置于toFindMax并在左侧子树中搜索最右侧的节点。
  • 如果遇到另一个要删除的节点,则将所有变量推入堆栈,并在删除该节点时将其弹出。
  • 当我们在左侧子树中找到最大值时,将保存对其及其父级的引用。
    然后,当我们递归返回到初始节点以进行删除时,我们将其删除(将其交换为最大值)。
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);
            }
        }
    }
}

我不知道为什么后遍历会是O(n 2 )。 删除单个节点的原因是O(n),因为您需要遍历树以找到该节点,这是O(n)操作。 但是一旦找到一个节点,就可以在O(1)时间内将其删除。 *因此,您可以在O(n)时间内一次遍历删除所有O(n)标记的节点。

*除非您需要维护平衡的树。 但是,您没有将其列为要求。

编辑正如@njlarsson在其注释中正确指出的那样,即使在找到节点之后,删除操作通常也不是O(1)。 但是,由于在访问要删除的节点之前要遍历左右子树,因此在遍历子树期间可以免费获得子树的最小(或最大)元素。 这将启用O(1)删除。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM