I understand the idea when deleting a node that has two subtrees: I "erase" the node's value and replace it with either its predecessor from the left subtree's value or its successor from the right subtree's value, and then delete that node.
However, does it matter if I choose the successor from the right subtree or the predecessor from the left subtree? Or is either way valid as long as I still have a binary search tree after performing the deletion?
Both ways to perform a delete operation are valid if the node has two children.
Remember that when you get either the in-order predecessor node or the in-order successor node, you must call the delete operation on that node.
It doesn't matter which one you choose to replace. In fact you may need both.
Look at the following BST.
7
/ \
4 10
/ \ /
1 5 8
\
3
To delete 1, you need to replace 1 with right node 3.
And to delete 10, you need to replace 10 with left node 8.
Related
As we know that to delete any node in BST we replace that deleted node with its inorder predecessor or successor.
I've tried a new approach in which I replace the deleted node either by its direct left child or by its direct right child (instead of replacing by its inorder pred. or succ.). And I think that this approach is valid for every node in BST. Program for this approach will be also easy as less number of links are changed for a node.I am attaching 2 pics to make you understand my approach.
Is my approach of deleting a node in BST is right or wrong? If wrong then why?
This can get pretty complicated, but you'll probably want to look into some rotations.
Say you have a full tree with 5 levels, and you are trying to delete the roots right child, which contains a quite a few more nodes. The issue is that by simply replacing with it's left or right child, would result in the deleted index "having" more than two child, which as I'm sure you know, is an invalid BST.
The solution? Rotations!
Here are a couple links that explain with some pictures.
http://www.mathcs.emory.edu/~cheung/Courses/171/Syllabus/9-BinTree/BST-delete2.html
While this is a valid way to delete a node in a binary tree, it won't always work for binary search trees. Let's say you want to delete 40 and 35 has a right child, by connecting 35 to 45, you'll be losing its original right child and every other node connected to it (you'll be losing a subtree). For binary search trees (BST), its better to replace the node with the rightmost node of the left subtree, which in this case is still 35 (this guarantees it does not have a right child) or the leftmost node of the right subtree if there is no left subtree.
If I construct a binary search tree as I have below, is it right? Or is such a tree not a valid binary search tree?
7
/ \
5 8
/ \
4 10
Because I have followed the rule of smaller element on the left and larger element on the right. So I imagine this is a binary search tree?
Try to walk the tree manually; pretending that you're a function looking for the 4.
You start at the root of the tree: the 7.
4 is less than 7, so you go left to 5.
4 is less than 5 so you go left again.
There's nothing there, so you'd come to the conclusion that 4 is not in the tree.
Of course that's incorrect though, so no, this is not a valid setup for a BST. It isn't simply that each value needs to only be in the correct position relative to its direct parent. Each value also needs to be in a correct position relative to the other nodes in the tree.
The exact position depends on the insertion order of each value, but with this configuration of values, the 4 should be to the left of the 5.
It's definitely a binary tree, as each node only has two leaves.
But like others said, it's not a binary search tree. A way of judging is for each node, all left leaves' values should be smaller than self's value and all right leaves' values should be greater than self's value. Only in this way can perform binary search.
In the case of Binary Search Trees why cannot we simply put the predecessor in place of the successor of a node in deletion case where a node is having two children?
We'd like to delete such a node with minimum amount of work and disruption to the structure of the tree.
Suppose we want to delete the node containing 6 from the following tree:
The standard solution is based on this idea: we leave the node containing 6 exactly where it is, but we get rid of the value 6 and find another value to store in the 6 node. This value is taken from a node below the 6s node, and it is that node that is actually removed from the tree.
Now, what value can we move into the vacated node and have a binary search tree? Well, here's how to figure it out. If we choose value X, then:
everything in the left subtree must be smaller than X.
everything in the right subtree must be bigger than X.
Let's suppose we're going to get X from the left subtree. (2) is guaranteed because everything in the left subtree is smaller than everything in the right subtree. What about (1)? If X is coming from the left subtree, (1) says that there is a unique choice for X - we must choose X to be the largest value in the left subtree. In our example, 3 is the largest value in the left subtree. So if we put 3 in the vacated node and delete it from its current position we will have a BST with 6 deleted.
The result is :
why cannot we simply put the predecessor in place of the successor of a node in deletion
case where a node is having two children?
We can put both and it is not necessary to replace the deleted node with the inorder successor. This is because in either case, the general contract of a BST is maintained.
Case1. Replace the deleted node with the inorder successor.
This is done by finding the leftmost node in the deleted node's right subtree.
Case2. Replace the deleted node with the inorder predecessor.
This is done by finding the rightmost node in the deleted node's left subtree.
Note that both these cases will keep all the elements in the left subtree smaller and all elements in right subtree greater than the element that we have brought into the position of the deleted node.
Consider the deletion procedure on a BST, when the node to delete has two children. Let's say i always replace it with the node holding the minimum key in its right subtree.
The question is: is this procedure commutative? That is, deleting x and then y has the same result than deleting first y and then x?
I think the answer is no, but i can't find a counterexample, nor figure out any valid reasoning.
EDIT:
Maybe i've got to be clearer.
Consider the transplant(node x, node y) procedure: it replace x with y (and its subtree).
So, if i want to delete a node (say x) which has two children i replace it with the node holding the minimum key in its right subtree:
y = minimum(x.right)
transplant(y, y.right) // extracts the minimum (it doesn't have left child)
y.right = x.right
y.left = x.left
transplant(x,y)
The question was how to prove the procedure above is not commutative.
Deletion (in general) is not commutative. Here is a counterexample:
4
/ \
3 7
/
6
What if we delete 4 and then 3?
When we delete 4, we get 6 as the new root:
6
/ \
3 7
Deleting 3 doesn't change the tree, but gives us this:
6
\
7
What if we delete 3 and then 4?
When we delete 3 the tree doesn't change:
4
\
7
/
6
However, when we now delete 4, the new root becomes 7:
7
/
6
The two resulting trees are not the same, therefore deletion is not commutative.
UPDATE
I didn't read the restriction that this is when you always delete a node with 2 children. My solution is for the general case. I'll update it if/when I can find a counter-example.
ANOTHER UPDATE
I don't have concrete proof, but I'm going to hazard a guess:
In the general case, you handle deletions differently based on whether you have two children, one child, or no children. In the counter-example I provided, I first delete a node with two children and then a node with one child. After that, I delete a node with no children and then another node with one child.
In the special case of only deleting nodes with two children, you want to consider the case where both nodes are in the same sub-tree (since it wouldn't matter if they are in different sub-trees; you can be sure that the overall structure won't change based on the order of deletion). What you really need to prove is whether the order of deletion of nodes in the same sub-tree, where each node has two children, matters.
Consider two nodes A and B where A is an ancestor of B. Then you can further refine the question to be:
Is deletion commutative when you are considering the deletion of two nodes from a Binary Search Tree which have a ancestor-descendant relationship to each other (this would imply that they are in the same sub-tree)?
When you delete a node (let's say A), you traverse the right sub-tree to find the minimum element. This node will be a leaf node and can never be equal to B (because B has two children and cannot be a leaf node). You would then replace the value of A with the value of this leaf-node. What this means is that the only structural change to the tree is the replacement of A's value with the value of the leaf-node, and the loss of the leaf-node.
The same process is involved for B. That is, you replace the value of the node and replace a leaf-node. So in general, when you delete a node with two children, the only structural change is the change in value of the node you are deleting, and the deletion of the leaf node who's value you are using as replacement.
So the question is further refined:
Can you guarantee that you will always get the same replacement node regardless of the order of deletion (when you are always deleting a node with two children)?
The answer (I think) is yes. Why? Here are a few observations:
Let's say you delete the descendant node first and the ancestor node second. The sub-tree that was modified when you deleted the descendant node is not in the left sub-tree of the ancestor node's right child. This means that this sub-tree remains unaffected. What this also means is regardless of the order of deletion, two different sub-trees are modified and therefore the operation is commutative.
Again, let's say you delete the descendant node first and the ancestor node second. The sub-tree that was modified when you deleted the descendant node is in the left sub-tree of the ancestor node's right child. But even here, there is no overlap. The reason is when you delete the descendant node first, you look at the left sub-tree of the descendant node's right child. When you then delete the ancestor node, you will never go down that sub-tree since you will always be going towards the left after you enter the ancestor node's right-child's left sub-tree. So again, regardless of what you delete first you are modifying different sub-trees and so it appears order doesn't matter.
Another case is if you delete the ancestor node first and you find that the minimum node is a child of the descendant node. This means that the descendant node will end up with one child, and deleting the one child is trivial. Now consider the case where in this scenario, you deleted the descendant node first. Then you would replace the value of the descendant node with its right child and then delete the right child. Then when you delete the ancestor node, you end up finding the same minimum node (the old deleted node's left child, which is also the replaced node's left child). Either way, you end up with the same structure.
This is not a rigorous proof; these are just some observations I've made. By all means, feel free to poke holes!
It seems to me that the counterexample shown in Vivin's answer is the sole case of non-commutativity, and that it is indeed eliminated by the restriction that only nodes with two children can be deleted.
But it can also be eliminated if we discard what appears to be one of Vivin's premises, which is that we should traverse the right subtree as little as possible to find any acceptable successor. If, instead, we always promote the smallest node in the right subtree as the successor, regardless of how far away it turns out to be located, then even if we relax the restriction on deleting nodes with fewer than two children, Vivin's result
7
/
6
is never reached if we start at
4
/ \
3 7
/
6
Instead, we would first delete 3 (without successor) and then delete 4 (with 6 as successor), yielding
6
\
7
which is the same as if the order of deletion were reversed.
Deletion would then be commutative, and I think it is always commutative, given the premise I have named (successor is always smallest node in right subtree of deleted node).
I do not have a formal proof to offer, merely an enumeration of cases:
If the two nodes to be deleted are in different subtrees, then deletion of one does not affect the other. Only when they are in the same path can the order of deletion possibly affect the outcome.
So any effect on commutativity can come only when an ancestor node and one of its descendants are both deleted. Now, how does their vertical relationship affect commutativity?
Descendant in the left subtree of the ancestor. This situation will not affect commutativity because the successor comes from the right subtree and cannot affect the left subtree at all.
Descendant in the right subtree of the ancestor. If the ancestor's successor is always the smallest node in the right subtree, then order of deletion cannot change the choice of successor, no matter what descendant is deleted before or after the ancestor. Even if the successor to the ancestor turns out to be the descendant node that is also to be deleted, that descendant too is replaced with the the next-largest node to it, and that descendant cannot have its own left subtree remaining to be dealt with. So deletion of an ancestor and any right-subtree descendant will always be commutative.
I think there are two equally viable ways to delete a node, when it has 2 children: SKIP TO CASE 4...
Case 1: delete 3 (Leaf node)
2 3
/ \ --> / \
1 3 1
Case 2: delete 2 (Left child node)
2 3
/ \ --> / \
1 3 1
Case 3: delete 2 (Right child node)
2 2
/ \ --> / \
1 3 3
______________________________________________________________________
Case 4: delete 2 (Left & Right child nodes)
2 2 3
/ \ --> / \ or / \
1 3 1 3
BOTH WORK and have different resulting trees :)
______________________________________________________________________
As algorithm explained here: http://www.mathcs.emory.edu/~cheung/Courses/323/Syllabus/Trees/AVL-delete.html
Deleting a node with 2 children nodes:
1) Replace the (to-delete) node with its in-order predecessor or in-order successor
2) Then delete the in-order predecessor or in-order successor
I respond here to Vivin's second update.
I think this is a good recast of the question:
Is deletion commutative when you are
considering the deletion of two nodes
from a Binary Search Tree which have a
ancestor-descendant relationship to
each other (this would imply that they
are in the same sub-tree)?
but this bold sentence below is not true:
When you delete a node (let's say A),
you traverse the right sub-tree to
find the minimum element. This node
will be a leaf node and can never be equal to B
since the minimum element in A's right subtree can have a right child. So, it is not a leaf.
Let's call the minimum element in A's right subtree successor(A).
Now, it is true that B cannot be successor(A), but it can be in its right subtree. So, it is a mess.
I try to summarize.
Hypothesis:
A and B have two children each.
A and B are in the same subtree.
Other stuff we can deduce from hypothesis:
B is not successor(A), neither A is successor(B).
Now, given that, i think there are 4 different cases (as usual, let be A an ancestor of B):
B is in A's left subtree
B is an ancestor of successor(A)
successor(A) is an ancestor of B
B and successor(A) don't have any relationship. (they are in different A's subtrees)
I think (but of course i cannot prove it) that cases 1, 2 and 4 don't matter.
So, only in the case successor(A) is an ancestor of B deletion procedure could not be commutative. Or could it?
I pass the ball : )
Regards.
I am reading through the binary tree delete node algorithm used in the book Data Structures and Algorithms: Annotated Reference with Examples
on page 34 , case 4(Delete node which has both right and left sub trees), following algorithm described in the book looks doesn't work, probably I may be wrong could someone help me what I am missing.
//Case 4
get largestValue from nodeToRemove.Left
FindParent(largestValue).Right <- 0
nodeToRemove.Value<-largestValue.Value
How does the following line deletes the largest value from sub tree FindParent(largestValue).Right <- 0
When deleting a node with two children, you can either choose its in-order successor node or its in-order predecessor node. In this case it's finding the the largest value in the left sub-tree (meaning the right-most child of its left sub-tree), which means that it is finding the node's in-order predecessor node.
Once you find the replacement node, you don't actually delete the node to be deleted. Instead you take the value from the successor node and store that value in the node you want to delete. Then, you delete the successor node. In doing so you preserve the binary search-tree property since you can be sure that the node you selected will have a value that is lower than the values of all the children in the original node's left sub-tree, and greater that than the values of all the children in the original node's right sub-tree.
EDIT
After reading your question a little more, I think I have found the problem.
Typically what you have in addition to the delete function is a replace function that replaces the node in question. I think you need to change this line of code:
FindParent(largestValue).Right <- 0
to:
FindParent(largestValue).Right <- largestValue.Left
If the largestValue node doesn't have a left child, you simply get null or 0. If it does have a left child, that child becomes a replacement for the largestValue node. So you're right; the code doesn't take into account the scenario that the largestValue node might have a left child.
Another EDIT
Since you've only posted a snippet, I'm not sure what the context of the code is. But the snippet as posted does seem to have the problem you suggest (replacing the wrong node). Usually, there are three cases, but I notice that the comment in your snippet says //Case 4 (so maybe there is some other context).
Earlier, I alluded to the fact that delete usually comes with a replace. So if you find the largestValue node, you delete it according to the two simple cases (node with no children, and node with one child). So if you're looking at pseudo-code to delete a node with two children, this is what you'll do:
get largestValue from nodeToRemove.Left
nodeToRemove.Value <- largestValue.Value
//now replace largestValue with largestValue.Left
if largestValue = largestValue.Parent.Left then
largestValue.Parent.Left <- largestValue.Left //is largestValue a left child?
else //largestValue must be a right child
largestValue.Parent.Right <- largestValue.Left
if largestValue.Left is not null then
largestValue.Left.Parent <- largestValue.Parent
I find it strange that a Data Structures And Algorithms book would leave out this part, so I am inclined to think that the book has further split up the deletion into a few more cases (since there are three standard cases) to make it easier to understand.
To prove that the above code works, consider the following tree:
8
/ \
7 9
Let's say that you want to delete 8. You try to find largestValue from nodeToRemove.Left. This gives you 7 since the left sub-tree only has one child.
Then you do:
nodeToRemove.Value <- largestValue.Value
Which means:
8.value <- 7.Value
or
8.Value <- 7
So now your tree looks like this:
7
/ \
7 9
You need to get rid of the replacement node and so you're going to replace largestValue with largestValue.Left (which is null). So first you find out what kind of child 7 is:
if largestValue = largestValue.Parent.Left then
Which means:
if 7 = 7.Parent.Left then
or:
if 7 = 8.Left then
Since 7 is 8's left child, need to replace 8.Left with 7.Right (largestValue.Parent.Left <- largestValue.Left). Since 7 has no children, 7.Left is null. So largestValue.Parent.Left gets assigned to null (which effectively removes its left child). So this means that you end up with the following tree:
7
\
9
The idea is to simply take the value from the largest node on the left hand side and move it to the node that is being deleted, i.e., don't delete the node at all, just replace it's contents. Then you prune out the node with the value you moved into the "deleted" node. This maintains the tree ordering with every node's value larger than all of it's left children and smaller than all of it's right children.
I think you may need to clarify what doesn't work.
I will try and explain the concept of deletion in a binary tree in case this helps.
Lets assume that you have a node in the tree that has two child nodes that you wish to delete.
in the tree below lets say that you want to delete node b
a
/ \
b c
/ \ / \
d e f g
When we delete a node we need to reattach its dependant nodes.
ie. When we delete b we need to reattach nodes d and e.
We know that the left nodes are less than the right nodes in value and that the parent nodes are between the left and right node s in value. In this case d < b and b < e. This is part of the definition of a binary tree.
What is slightly less obvious is that e < a. So this means that we can replace b with e. Now we have reattached e we need to reattach d.
As stated before d < e so we can attach e as the left node of e.
The deletion is now complete.
( btw The process of moving a node up the tree and rearranging the dependant nodes in this fashion is known as promoting a node. You can also promote a node without deleting other nodes.)
a
/ \
d c
\ / \
e f g
Note that there is another perfectly legitimate outcome of deleteing node b.
If we chose to promote node d instead of node e the tree would look like this.
a
/ \
e c
/ / \
d f g
If I understand the pseudo-code, it works in the general case, but fails in the "one node in the left subtree" case. Nice catch.
It effectively replaces the node_to_remove with largest_value from it's left subtree (also nulls the old largest_value node).
Note that in a BST, the left subtree of node_to_remove will be all be smaller than node_to_remove. The right subtree of node_to_remove will all be larger than node_to_remove. So if you take the largest node in the left subtree, it will preserve the invariant.
If this is a "one node in the subtree case", it'll destroy the right subtree instead. Lame :(
As Vivin points out, it also fails to reattach left children of largestNode.
It may make more sense when you look at the Wikipedia's take on that part of the algorithm:
Deleting a node with two children:
Call the node to be deleted "N". Do
not delete N. Instead, choose either
its in-order successor node or its
in-order predecessor node, "R".
Replace the value of N with the value
of R, then delete R. (Note: R itself
has up to one child.)
Note that the given algorithm chooses the in-order predecessor node.
Edit: what appears to be missing the possibility that R (to use Wikipedia's terminology) has one child. A recursive delete might work better.