考虑一个二叉搜索树,其中所有键都是唯一的。节点没有父指针。
我们有多达 n/2 个标记的节点。
我可以在 O(n 2 ) 时间删除所有这些(使用后序遍历,当遇到标记节点时,在 O(n) 处删除每个)。但这是不合适的。我需要一种算法来在O(n)时间删除所有标记的节点。谢谢。
编辑删除后,我需要保持节点顺序不变。
EDIT-2所以看起来我已经使用典型的删除删除了每个标记的节点(在左子树中找到最右边的节点并将其与要删除的节点交换)。
问问题
3010 次
3 回答
6
有很多方法,但这里有一个应该很容易正确,并给你一个完美平衡的树作为副作用。但是,它需要线性的额外空间。
- 创建一个大小为 N 减去标记节点数的数组(或 N,如果您不知道标记了多少并且不想检查它)。
- 将元素按顺序放置在数组中,通过中序遍历,跳过标记的元素。这需要线性时间,并且堆栈空间与树的高度成正比。
- 使用数组自上而下重建树。数组中的中间元素成为新的根,左子数组的中间元素成为新的左子元素,右子数组的中间元素成为新的右子子元素。递归地重复。这需要线性时间和对数堆栈空间。
更新:忘了说跳过标记的元素,但这很明显,对吧?;)
于 2012-05-08T15:34:01.183 回答
2
我已经找到了怎么做!
- 我们使用中序遍历。
- 我们在函数的递归调用之前检查是否需要删除节点。
- 当我们找到要删除的节点时,我们将标记 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);
}
}
}
}
于 2012-05-09T13:26:37.980 回答
1
我不明白为什么后序遍历会是 O(n 2 )。之所以删除单个节点是O(n),是因为需要遍历树才能找到节点,这是一个O(n)的操作。但是一旦你找到一个节点,它可以在 O(1) 时间内被删除。*因此,您可以在 O(n) 时间内在一次遍历中删除所有 O(n) 标记的节点。
*除非你需要保持平衡的树。但是,您没有将其列为要求。
编辑正如@njlarsson 在他的评论中正确指出的那样,即使在找到节点之后,删除操作通常也不是 O(1)。但是,由于在访问要删除的节点之前已经遍历了左右子树,因此在子树遍历期间可以免费获得子树的最小(或最大)元素。这启用了 O(1) 删除。
于 2012-05-08T15:22:35.687 回答