I want to make a post-order traversal in the thread binary tree.
If I want to do a post-order traversal, not a recursive one,
For terminal nodes, I can move them by inserting the next node address in the link to an empty child node field.
But in the case of parent node must be moved any way if I don't get the hang of things constantly.
For example, in the case of "A B * C D / -", I can go 'B' -> '*' to use B's right empty link field(thread).
But when I go '*' -> 'C' or '/' -> '-'
'*' and '/' both link fields are full by child node. I don't know how to proceed.
Please let me know how you can proceed.
Parent node's links that use tree links (doesn't use thread links) are fulled.
How can I go to Parent node's Parent node.
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.
Can someone explain it in detail?(it's forest ,not binary tree)
I'm reading the tree and forest chapter of data structure book,I've known the pre order traversal and in order traversal of forest.
Post traversal is a kind of method to explore your tree or forest.
in detail, post traversal mean you will explore nodes nearest to the leaf including the leaf itself.
In action; you should always go to your first or leftmost node until the current node does not have any child. if that occurs, you do your actions with the note , go to his parent, go to "his next son" and the you keep going deep into that node repeting the process (first leftmost child, etc...).
Heres an image that can help you understand
The tree I am going to be using consists of nodes with a left child and a right sibling. Besides the left child, siblings will not be connected to the parent node.
I need to reconstruct the tree from pre-order list and post-order list.
Example - The first line below is the pre-order, the second is the post-order.
QRUVSTWXZY
UVRSWZXYTQ
The tree would look like this:
Q
/ | \
R S T
/ \ / | \
U V W X Y
|
Z
I have a vague idea about how I can go through those two lists and construct a tree, but my logic is keep bumping into walls.
Can anyone suggest an algorithm or method that could go through two lists and find a structure?
All you need to do is compare both the lists,we use a simple idea that a parent node will always be before its child in pre order and in post order its the inverse.
step1
if first element in pre-order is equal to the last element of post order its the root node(parent node) of the left out list and remove it from both lists.
Qruvstwxzy-->ruvstwxzy
uvrswzxytQ-->uvrswzxyt
step2
now compare both lists(new) the element in first position is a immediate child of the previously removed node now seperate the string ass a substring till the same element occurs in the post order list. now do the same with now leftout strings iteratively
Ruvstwxzy--> Ruv S Twxzy
uvRswzxyt--> uvR S wzxyT
step3
if in this iteration we are getting a string with one element then its a leaf node and the immediate child to the parent noed of this whole string.
By doing this iteratively we will get the exact tree which you want to get.
Theorem 1: If and only if node A appears before node B in the pre-order, and after node B in the post-order, node A is an ancestor of node B.
Based on this, for any two nodes, we can determine whether one is an ancestor of the other or not.
Using this, we first place the root R (the firstmost node in the preorder). Then we place the first child A1 of the root (the secondmost node in the preorder). Now using theorem 1, we can determine which other nodes are somewhere below A1. All those nodes will appear after A1 in the pre-order, and before A1 in the post-order. Among the other nodes (which are not below A1), the first one to appear in the pre-order, is also a direct child of R. Let's call it A2. We can again find out which other nodes are somewhere below A2. Then place A3. And so on.
This way, we've rebuilt the tree down to the first level (A1, A2, A3,...An), and we know which nodes are somewhere below A1, which nodes are somewhere below A2, and so on. Now we can follow the above process recursively for each of A1, A2, etc and we shall have the tree!
We built a list from a tree (not necessarily a binary search tree) via depth first traversal.
Each entry inside is a pair (k, d), k is the key of a node and d is the depth of that node.
Now we need to construct the original tree back from the list.
How do we do it?
Note
tree is not necessarily a binary search tree
we do not know whether the depth first traversal is pre-order, in-order or post-order.
My question is
Can we achieve this reverse engineering under the conditions? I know for binary search tree, we need at least two traversal lists (e.g., inorder and postorder list) to reconstruct the original tree.
How? if possible
Things to note:
The in-order traversal produces a unique tree
The pre-order and post-order don't:
You can't differentiate between these two:
1 1
/ \
2 2
I'll just generate the one on the left (doing this makes it a lot easier).
What we can say right away:
If the first node is the root (i.e. not depth 0):
We're either doing in-order with an empty left subtree, or pre-order.
If the last node is the root:
We're either doing in-order with an empty right subtree, or post-order.
If neither of the above:
We're doing in-order traversal.
For the two cases above where we don't know which traversal to do, the simplest approach is to try to generate the trees for both possible traversals, and discard whichever one doesn't work (based on the below restrictions), if either.
Some restrictions:
For in-order, we can't go right or up if the current node is empty.
For pre-order, we can't go left or right if the current node is empty.
For post-order, we have to go up after setting the current node - we can't go left or right without having set the current node.
In all cases, we try to go left before going right before going up.
By 'go left' or 'go right', I mean creating an (empty) left or right child and traversing to that node.
By 'go up', I mean simply traversing upwards in the already created tree.
Based on the above restrictions, it should be easy to write an algorithm to generate the tree. As an example for in-order:
If the new node's depth is deeper than the current node's depth:
If the current node is empty and doesn't have a left child, we can just create a left child and set that as the current node
Otherwise, if the current node is not empty and doesn't have a right child, we can just create a right child and set that as the current node
Otherwise, if the depth is the same as the current node and the current node is empty,
set that node's value to the new node
If none of the above cases triggered and the current node is empty,
set the parent of the current node as the current node
If none of the above cases triggered, fail
If 1.1, 1.2, or 3 triggered, repeat from 1.
Example:
Input: (f, 2), (g, 2), (b, 1), (i, 2), (c, 1), (a, 0)
Since (a, 0) is the root, we're doing either in-order or post-order.
So then we generate 2 subtrees:
in-order post-order
. .
/ /
. .
/ /
f f
(. indicates an empty node)
When we get (g, 2), we can already discard the in-order tree, as we can't go right or up from f's parent, because it is empty, so we're stuck.
Then we continue with post-order:
.
/
.
/ \
f g
.
/
b
/ \
f g
.
/ \
b .
/ \ /
f g i
.
/ \
b c
/ \ /
f g i
a
/ \
b c
/ \ /
f g i
I'm not sure what you mean by pre/post/in-order, a single DFS run with the depth data should allow you to reconstruct the tree if you know the time of first visiting each node (I guess that would amount to a "pre-order" by your definition). In-order isn't even defined well in non-binary trees (would the parent appear after the first node? after the second? what if there's a single child?)
If you can tell the order of discovering each node, you can just go over the list while the depth increases, creating more and more children (and keeping track of the last node encountered at each depth), and once you get a non increasing depth you know for sure how many levels up you need to go in order to place the next node.
Two list-consecutive nodes with the same depth would be siblings of the same parent, and in general, if the last node had depth d1 and you now encounter d2, then you need to go up d1-d2+1 levels back up the current branch before attaching the next node.
A depth of d is enough to identify the parent (it would have to be the last node of depth d-1), since in DFS you can't encounter any other parent of that depth without first completely exploring the entire branch descending from from the previous one.
A slightly better proof - let v be a node along your list of depth d. It would have to descend of some node of depth d-1.
Let's assume the list is
[(v0,d0), ... (v, d), ...]
The parent can't be in the remainder of the list since this means you reached a child before its parent - impossible while traversing a tree. So the parent has to be in the first ... section. Let's assume it's not the last d-1-depth node prior to v - so let's say the list is -
[(v0,d0), ... (vi, d-1), ... (vj, d-1), ...(v, d), ...]
if v is the child of vi, than when traversing the original tree your DFS reached vi, missed v somehow, then passed to another branch originating from some ancestor of vi, found vj there, and only then came back to vi and encountered v. This violates the DFS premise.
Maybe I'm missing something, but:
If the first pair in the sequence has depth zero, it's a pre-order traversal.
If the last pair in the sequence has depth zero, it's a post-order traversal.
Else it's an in-order traversal.
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.