I've created Raw BinaryTree. In this tree, insertion is not like BST, its like this::
If tree is empty then add value & make it root. (suppose 30)
If tree is not empty then input father value (30) & add new value (20) to its left subtree.
If left subtree is not empty then, insert value (20) to right subtree.
For next insertion, again take father value to determine where value is to be added.
& so on..
Its working fine except when I try to delete a node with two children. Method Im using to delete is deleteWithCopy.
As my instructor has told, deletewithcopy is:
1. Find father of node (temp) which is to be deleted.
2. If temp (node to be deleted) is the right child of 'father' then find temp's immediate Successor
3. If temp (node to be deleted) is the left child of 'father' then find temp's immediate Predecessor
4. Swap value of temp with its predecessor/succesor
5. Delete temp (which is now leaf of tree).
NOW How to find Successor & Predecessor.
Successor = logical successor of a node is its right-most child in left subtree
Predecessor = logical predecessor of a node is its left-most child in right subtree
According to algorithm I have created the function, but after deleting, when I traverse (or print) the tree, it shows run time error,
Unhandled exception at 0x008B5853 in binarytree.exe: 0xC0000005: Access violation reading location 0xFEEEFEEE.
which is error for "0xFEEEFEEE is used to mark freed memory in Visual C++."
I have dry run-ed this thing again & again, there is nothing out of bounds in memory that Im trying to acces, I have fixed every loose end, but still :(
Here is the function:
void BinaryTree<mytype>::deletewithTwoChild(BTNode<mytype> *temp)
{
BTNode<mytype> *father = findfather(temp, root); //found address of father of temp node & stored it in pointer
BTNode<mytype> *leaf = temp; //created a copy of temp node
/////CASE 1 (for predecessor)
if(temp==root || father->left==temp) //if temp is left child of its father then
{
leaf = leaf->left; //move leaf 1 time left
while(leaf->right!=0 ) //until leaf reaches the right most node of left subtree
{
leaf = leaf->right; //move leaf 1 time to right
}
//swapping values
mytype var = leaf->key_value; //created a template variable to store leaf's key
leaf->key_value = temp->key_value; //assigning temp's key to leaf
temp->key_value = var; //assigning leaf's key to temp
if(leaf->right!=0) //if leaf has right child then call deletewithOneChild function
{
deletewithOneChild(leaf); //call to respective function
}
else if(leaf->left==0 && leaf->right==0) //if leaf has no children then
{
deleteWithNoChild(leaf); //call to respective function
}
}
/////CASE 2 (for successor)
else if(father->right==temp) //if temp is right child of its father, then
{
leaf = leaf->right; //move leaf 1 time right
while(leaf->left!=0) //until leaf reaches the last node of tree which has no child
{
leaf = leaf->left; //move leaf 1 time to left
}
//swapping values
mytype var = leaf->key_value; //created a template variable to store leaf's key
leaf->key_value = temp->key_value; //assigning temp's key to leaf
temp->key_value = var; //assigning leaf's key to temp
if(leaf->right!=0) //if leaf has right child then call deletewithOneChild function
{
deletewithOneChild(leaf); //call to respective function
}
else if(leaf->left==0 && leaf->right==0) //if leaf has no children then
{
deleteWithNoChild(leaf); //call to respective function
}
}
}
Data Set I m using:
30
/ \
20 80
/ / \
10 40 120
\ / \
60 100 140
/ \ / \
50 70 130 150
Im trying to delete node 80, 60, 120, 140 when the run time error pops up. Plz help :(( Also I need guidence how to handle tree iff 30 is deleted.
As I know, the definition of successor and predecessor are different.
Successor : the minimum node of right subtree, that is, the left-most node of right subtree.
Predecessor : the maximum node of left subtree, that is, the right-most node of left subtree.
Back to Issue 1. I have noticed the if-else condition after swapping values in case 1. Since, in case 1, you are finding the right most node of the subtree, leaf->right always be null and leaf->left may not be null. As a result, none of the delete function be called in case after swapping values. This will cause the issue of wrong BST or even worse, the crash of the program. Therefore, the if-else condition in case would be:
// leaf->right always be null, only need to verify leaf->left.
if (leaf->left != 0)
{
deleteWithOneNode(leaf);
}
else
{
deleteWithNoChild(leaf);
}
Issue 2. As I know, the remove of one node in BST has no rule to choose predecessor or successor and the parent(father) node of the removed node is used only when swapping the whole node rather then swapping the value of the node only. Therefore, my delete function would be:
void BinaryTree<myType>::delete(BTNode<myType>* node, BTNode<myType>* parent)
{
if (node->right) // find successor first
{
BTNode* ptrParent = node;
BTNode* ptr = node->right;
while (ptr->left)
{
ptrParnet = ptr;
ptr = ptr->left;
}
// Since the ptr will be delete, we only assign the value of ptr to the value node
node->key_value = ptr->key_value;
if (node == ptrParent)
{
ptrParnet->right = ptr->right;
}
else
{
ptrParent->left = ptr->right;
}
delete ptr;
}
else if (node->left) // find predecessor
{
BTNode* ptrParent = node;
BTNode* ptr = node->left;
while (ptr->right)
{
ptrParent = ptr;
ptr = ptr->right;
}
// Since the ptr will be delete, we only assign the value of ptr to the value node
node->key_value = ptr->key_value;
if (node == ptrParent)
{
ptrParent->left = ptr->left;
}
else
{
ptrParent->right = ptr->left;
}
delete ptr;
}
else
{
if (node->key_value > parent->key_value)
{
parent->right = NULL;
}
else
{
parent->left = NULL;
}
delete node;
}
}
By using the function, the tree after removing 30 would be
40
/ \
20 80
/ / \
10 60 120
/ \ / \
50 70 100 140
/ \
130 150
Related
I'm trying to make complete tree from scratch in C++:
1st node = root
2nd node = root->left
3rd node = root->right
4th node = root->left->left
5th node = root->left->right
6th node = root->right->left
7th node = root->right->right
where the tree would look something like this:
NODE
/ \
NODE NODE
/ \ / \
NODE NODE NODE NODE
/
NEXT NODE HERE
How would I go about detecting where the next node would go so that I can just use one function to add new nodes? For instance, the 8th node would be placed at root->left->left->left
The goal is to fit 100 nodes into the tree with a simple for loop with insert(Node *newnode) in it rather than doing one at a time. It would turn into something ugly like:
100th node = root->right->left->left->right->left->left
Use a queue data structure to accomplish building a complete binary tree. STL provides std::queue.
Example code, where the function would be used in a loop as you request. I assume that the queue is already created (i.e. memory is allocated for it):
// Pass double pointer for root, to preserve changes
void insert(struct node **root, int data, std::queue<node*>& q)
{
// New 'data' node
struct node *tmp = createNode(data);
// Empty tree, initialize it with 'tmp'
if (!*root)
*root = tmp;
else
{
// Get the front node of the queue.
struct node* front = q.front();
// If the left child of this front node doesn’t exist, set the
// left child as the new node.
if (!front->left)
front->left = tmp;
// If the right child of this front node doesn’t exist, set the
// right child as the new node.
else if (!front->right)
front->right = tmp;
// If the front node has both the left child and right child, pop it.
if (front && front->left && front->right)
q.pop();
}
// Enqueue() the new node for later insertions
q.push(tmp);
}
Suppose root is node#1, root's children are node#2 and node#3, and so on. Then the path to node#k can be found with the following algorithm:
Represent k as a binary value, k = { k_{n-1}, ..., k_0 }, where each k_i is 1 bit, i = {n-1} ... 0.
It takes n-1 steps to move from root to node#k, directed by the values of k_{n-2}, ..., k_0, where
if k_i = 0 then go left
if k_i = 1 then go right
For example, to insert node#11 (binary 1011) in a complete tree, you would insert it as root->left->right->right (as directed by 011 of the binary 1011).
Using the algorithm above, it should be straightforward to write a function that, given any k, insert node#k in a complete tree to the right location. The nodes don't even need to be inserted in-order as long as new nodes are detected created properly (i.e. as the correct left or right children, respectively).
Assuming tree is always complete we may use next recursion. It does not gives best perfomance, but it is easy to understand
Node* root;
Node*& getPtr(int index){
if(index==0){
return root;
}
if(index%2==1){
return (getPtr( (index-1)/2))->left;
}
else{
return (getPtr( (index-2)/2))->right;
}
}
and then you use it like
for(int i = 0; i<100; ++i){
getPtr(i) = new Node( generatevalue(i) );
}
private Node addRecursive(*Node current, int value) {
if (current == null) {
return new Node(value);
}
if (value < current.value) {
current->left = addRecursive(current->left, value);
} else if (value > current->value) {
current->right = addRecursive(current->right, value);
} else {
// value already exists
return current;
}
return current;
}
I do not know that if your Nodes has got a value instance but:
With this code you can have a sorted binary tree by starting from the root.
if the new node’s value is lower than the current node’s, we go to the left child. If the new node’s value is greater than the current node’s, we go to the right child. When the current node is null, we’ve reached a leaf node and we can insert the new node in that position.
Bear with me on this one because I think it's moderately hard to explain:
I have built the following AVLTree::Insert() proc, as per R.Coleman's implementation:
void AVLTree::Insert(AVLTreeNode *newNode) ///MARKER: ####### REMINDER ######
{ ///This implementation REQUIRES that newNode has its #left, #right and #parent pointers set to NULL *BEFORE* sending him here, as a parameter
AVLTreeNode *temp, *prev, *ancestor;
temp = root; //Our temp starts at the root, and keeps heading down the tree until it "falls out".
prev = NULL; //#prev will "follow" #temp, one step behind it. It will, in the end, mark the point where we'll add newNode at.
ancestor = NULL; //Ancestor marks the position of the closest #ancestor that will drop out of balance after we insert newNode.
if(root == NULL ) //Check if the tree is empty before you do anything else.
{
root = newNode;
return;
}
//Looks like it isn't empty. Let's start the main loop.
while(temp != NULL)
{
prev = temp;
if(temp->balanceFactor != '=') //We found a node that is unbalanced, it'll drop out of balance completelly when we add the new node.
{ //Let's have the #ancestor variable point at it so we can restore the AVL property from the bottom to this node.
ancestor = temp;
}
if(newNode->value < temp->value) //These two ifs will throw #temp out of the tree at the end of the loop
{ //while #prev will be pointing at the node below which we'll be adding newNode
temp = temp->left;
}
else
{
temp = temp->right;
}
}
///The loop finished, #temp is now null. Time to insert newNode.
newNode->parent = prev;
if(newNode->value < prev->value) //If it's smaller than #prev, place it on its left, else do it at #prev's right.
{
prev->left = newNode;
}
else
{
prev->right = newNode;
}
///Now to restore the AVL property of the tree, starting from the inserted node up towards #ancestor, the last known unbalanced node that has now completely fallen out of balance.
restoreAVL(ancestor,newNode);
}
Notice that in the end, I call RestoreAVL that takes as parameters the newNode and ancestor (the last node back up the tree that needs adjust because he has fallen out of balance - it gets pointed to a node during the while(temp!=null) loop.)
This is AVLTree::restoreAVL(): If you bother reading it all, it takes in account every case that can happen by inserting a new node to an an AVLTree and takes care to restore the AVL property, if needed, with rotations and re-set the balance factors (L, R or =)
void AVLTree::restoreAVL(AVLTreeNode *ancestor, AVLTreeNode *newNode)
{ ///This process restores the AVL property in the tree, from the bottom
//-------------------------------------------------------
// Case 1: ancestor is NULL, that means the balanceFactor of all ancestors is '='
//-------------------------------------------------------
if(ancestor == NULL)
{
if(newNode->value < root->value)
{
root->balanceFactor = 'L'; //newNode was inserted at the left of our root
} //during our previous Insert
else
{
root->balanceFactor = 'R'; //Here it's on our right
}
///Adjust the balanceFactor for all nodes from newNode back up to root
adjustBalanceFactors(root, newNode);
}
//-------------------------------------------------------
// Case 2: Insertion in opposite subtree of ancestor's balance factor, i.e.
// ancestor.balanceFactor == 'L' AND Insertion made in ancestor's RIGHT subtree
// OR
// ancestor.balanceFactor == 'R' AND Insertion made in ancestor's LEFT subtree
// (In short, the insertion "neutralises" the balance of ancestor.)
//-------------------------------------------------------
else if( ( (ancestor->balanceFactor == 'L') && (newNode->value > ancestor->value) )
||
( (ancestor->balanceFactor == 'R') && (newNode->value < ancestor->value) )
)
{
ancestor->balanceFactor = '='; //Ancestor's balance factor is now neutralised.
///Adjust the balanceFactor for all nodes up to the ancestor,
///not up to the root like we did in Case 1.
adjustBalanceFactors(ancestor,newNode);
}
//-------------------------------------------------------
// Case 3: #ancestor's balance is 'R' and the new node was inserted in the right subtree of #ancestor's right child.
// As expected, the balance is now broken and we need to rotate left, once.
//-------------------------------------------------------
else if( (ancestor->balanceFactor == 'R') && (newNode->value > ancestor->right->value) )
{
ancestor->balanceFactor = '='; //We reset #ancestor's balance, it will be adjusted by #adjustBalanceFactors()
rotateLeft(ancestor); //Single left rotation with ancestor as the pivot.
///Let's adjust the balanceFactor for all nodes up to #ancestor's PARENT.
adjustBalanceFactors(ancestor->parent, newNode);
}
//-------------------------------------------------------
// Case 4: #ancestor's balance is 'L' and the node inserted is in the left subtree of #ancestor's left child.
// Here we have to rotate right, once. (Mirror case of Case 3 - See above)
//-------------------------------------------------------
else if( (ancestor->balanceFactor == 'L') && (newNode->value < ancestor->left->value) )
{
ancestor->balanceFactor = '='; //As before, #ancestor's balance needs to be reset.
rotateRight(ancestor);
///Again, we adjust the balanceFactor for all nodes up to #ancestor's PARENT.
adjustBalanceFactors(ancestor->parent, newNode);
}
//-------------------------------------------------------
// Case 5: #ancestor's balance factor is "L" and the new node is inserted
// in the RIGHT subtree of ancestor's LEFT child
//-------------------------------------------------------
else if( (ancestor->balanceFactor == 'L') && (newNode->value > ancestor->left->value) )
{
rotateLeft(ancestor->left);
rotateRight(ancestor);
adjustLeftRight(ancestor,newNode);
}
//-------------------------------------------------------
// Case 6 (final case): #ancestor's balance factor is "R" and the new node is inserted
// in the LEFT subtree of ancestor's RIGHT child
//-------------------------------------------------------
else
{
rotateRight(ancestor->right);
rotateLeft(ancestor);
adjustRightLeft(ancestor,newNode);
}
}
So my question is: I want to implement AVLTree::Delete(AVLTreenode *n). Instead of busting my head thinking of every possible outcome if you delete a node in an AVLTree, can I reduce a Deletion() into an Insertion() case and call RestoreAVL() with some node set as newNode and one set as ancestor? Can I recycle restoreAVL()?
Some examples:
The result is the same if I think that, after ignoring 00, 20 is inserted in the subtree.
But let's add node 70 on the left tree, and try reducing the Deletion() into an Insertation.
I can't think of any algorithmic way of reducing this situation into an Insertation(), so I know who can act as newNode and who can be the ancestor, and call restoreAVL().
Is what I'm saying feasible? Is there a failsafe way of reducing the problem and thus reducing the code I have to rewrite?
If you can code insertion operator, you will be possible to code deletion. Because deletion is simplier than insertion. It is really true because you don't need to rebalance in deletion operator. Complexity is still O(log N) for each query.
My code is not too efficient but it is short enough to code.
void doDelete(node* a){
if (a==0) return ;
if (a->ll==0) {
if (a->pp==0 || a->pp->ll==a)
lljoin(a->rr, a->pp);
else rrjoin(a->rr, a->pp);
delete a;
}
else if (a->rr==0){
if (a->pp==0 || a->pp->ll==a)
lljoin(a->ll, a->pp);
else rrjoin(a->ll, a->pp);
delete a;
}
else {
node *b = rightMost(a->ll);
swap(b->value, a->value);
doDelete(b);
}
}
Obviously you can find some common actions for deletion and insertion (for example searching), but I think that's not worth trying. There are still a lot uncommon between this algorithms. You definetely will reuse restoreAVL for AVL property recovery.
As I understand, the problem in your examples is that when you delete node from one subtree, you want to balance another subtree of the last balanced node on the path to the deleted node from root (where balanced node is node with balancedFactor='='). To me It has no sense, because it is not correct on the first place and much trickier to code.
I want to make a queue using linked lists.
There are numerous algorithms out there for that. But what i'm curious in is how to make a relative priority queue.
Maybe there is a special name for this type of queue, but i don't know it, and i haven't had any luck googling for the solution.
Anyways, let's say i have this struct, which will represent the Node of my list.
struct Node {
int value;
Node* next;
}
if i want to create a priority queue (where the element with the least value is first), when i insert for example 5 7 1 8 2, my list should look like this:
1 -> 2 -> 5 -> 7 -> 8
It's not really hard to implement that.
What i want to do is - when i insert the first element, other elements should have value relative to the previous element. So, in my example, the list/queue would contain the following values:
1 -> 1 -> 3 -> 2 -> 1
I'm not really sure how i would implement that? Would the following idea be applicable:
in the Node struct i add another field, which would represent the original value.
i find the position of the node i'm inserting the same way i would do when creating an ordinary linked list, and then i just say
temp->value = temp->originalValue - previous->originalValue;
You need to store extra data in each node, either the relative priority, or a "previous" pointer. Since the next node's relative priority needs to updated whenever a node is removed (how to do that without a prev pointer?), I suggest the "previous" pointer:
struct Node {
int value;
Node* next;
Node* prev;
}
Then a function can evaluate the relative priority:
int relative_priority(Node* node) {
if (node == NULL)
return 0;
if (node->prev == NULL)
return node->value;
return node->value - node->prev->value;
}
Note that I'm using C, you'll need to replace NULL with 0 for C++
You first have to identify where to insert the new node. This involves decrements on the target value, adjusting its relative value in relation to the current in the list. At the point of insertion, you have to point the previous node to the new node, and then adjust the node ahead of the new node with a new relative value.
Node * create_node (int value, Node *next) { /* ... */ }
void insert_relative_priority_queue (Node **head, int value) {
Node **prev = head, *cur;
if (*head) {
cur = *head;
while (value > cur->value) {
value -= cur->value;
prev = &cur->next;
cur = cur->next;
if (cur == 0) break;
}
*prev = create_node(value, cur);
if (cur) {
cur->value -= value;
}
} else {
*head = create_node(value, 0);
}
}
When you remove from the front of the list, you adjust the value of the new head:
void remove_relative_priority_queue (Node **head) {
if (*head) {
Node *cur = *head;
*head = cur->next;
if (*head) {
(*head)->value += cur->value;
}
free(cur);
}
}
I am building a binary tree. The binary tree is pre-built in a file and I need to construct it. Due to the way it is structured, I read the tree into an array. Each tree nodes look something like this.
struct Tree_Node
{
float normalX;
float normalY;
float normalZ;
float splitdistance;
long region;
long left, right; //array index
Tree_Node* left_node; // pointer to left node
Tree_Node* right_node; // pointer to right node
} typedef Tree_Node;
I have tried a number of ways to write some code that will build the tree. Let me give you some pseudocode so you understand what I am trying to do.
Read in head node. Node is number one in the array.
If the node has a right and left array index, create new nodes and
insert the information from the array
indicies into that tree node.
If the node does not have a right and left index, it is a leaf node.
Here is my building function:
void WLD::treeInsert(BSP_Node *tree_root, int node_number)
{
/// Add the item to the binary sort tree to which the parameter
// "root" refers. Note that root is passed by reference since
// its value can change in the case where the tree is empty.
if ( tree_root == NULL )
{
// The tree is empty. Set root to point to a new node containing
// the new item. This becomes the only node in the tree.
tree_root = new BSP_Node();
tree_root->normalX = bsp_array[node_number].normal[0];
tree_root->normalY = bsp_array[node_number].normal[1];
tree_root->normalZ = bsp_array[node_number].normal[2];
tree_root->splitdistance = bsp_array[node_number].splitdistance;;
tree_root->region = bsp_array[node_number].region;
tree_root->left = bsp_array[node_number].left;
tree_root->right = bsp_array[node_number].right;
tree_root->left_node[node_number];
tree_root->right_node[node_number];
errorLog.OutputSuccess("Inserting new root node: %i", node_number);
// NOTE: The left and right subtrees of root
// are automatically set to NULL by the constructor.
// This is important...
}
if ( tree_root->left != 0 )
{
errorLog.OutputSuccess("Inserting left node number: %i!", tree_root->left);
treeInsert( tree_root->left_node, tree_root->left );
}
else if ( tree_root->right != 0 )
{
errorLog.OutputSuccess("Inserting right node: %i!", tree_root->right);
treeInsert( tree_root->right_node, tree_root->right );
}
else if ( tree_root->right == 0 && tree_root->left == 0)
{
errorLog.OutputSuccess("Reached a leaf node!");
return;
}
else
{
errorLog.OutputError("Unknown BSP tree error!");
}
}
My debug shows that the function tries to insert node 2 until the program crashes.
Can someone help me with this?
tree_root->left_node[node_number];
I don't see any code that initializes this array, so this'll be referring to something invalid.
Then by the time you come around to the next function
treeInsert( tree_root->left_node, tree_root->left );
treeInsert will be called with an invalid pointer, since left_node doesn't go anywhere.
I imagine you need something like tree_root->left_node = NULL instead of tree_root->left_node[node_number] so that the recursive call to treeInsert creates the next node.
Can someone please help me understand the following Morris inorder tree traversal algorithm without using stacks or recursion ? I was trying to understand how it works, but its just escaping me.
1. Initialize current as root
2. While current is not NULL
If current does not have left child
a. Print current’s data
b. Go to the right, i.e., current = current->right
Else
a. In current's left subtree, make current the right child of the rightmost node
b. Go to this left child, i.e., current = current->left
I understand the tree is modified in a way that the current node, is made the right child of the max node in right subtree and use this property for inorder traversal. But beyond that, I'm lost.
EDIT:
Found this accompanying c++ code. I was having a hard time to understand how the tree is restored after it is modified. The magic lies in else clause, which is hit once the right leaf is modified. See code for details:
/* Function to traverse binary tree without recursion and
without stack */
void MorrisTraversal(struct tNode *root)
{
struct tNode *current,*pre;
if(root == NULL)
return;
current = root;
while(current != NULL)
{
if(current->left == NULL)
{
printf(" %d ", current->data);
current = current->right;
}
else
{
/* Find the inorder predecessor of current */
pre = current->left;
while(pre->right != NULL && pre->right != current)
pre = pre->right;
/* Make current as right child of its inorder predecessor */
if(pre->right == NULL)
{
pre->right = current;
current = current->left;
}
// MAGIC OF RESTORING the Tree happens here:
/* Revert the changes made in if part to restore the original
tree i.e., fix the right child of predecssor */
else
{
pre->right = NULL;
printf(" %d ",current->data);
current = current->right;
} /* End of if condition pre->right == NULL */
} /* End of if condition current->left == NULL*/
} /* End of while */
}
If I am reading the algorithm right, this should be an example of how it works:
X
/ \
Y Z
/ \ / \
A B C D
First, X is the root, so it is initialized as current. X has a left child, so X is made the rightmost right child of X's left subtree -- the immediate predecessor to X in an inorder traversal. So X is made the right child of B, then current is set to Y. The tree now looks like this:
Y
/ \
A B
\
X
/ \
(Y) Z
/ \
C D
(Y) above refers to Y and all of its children, which are omitted for recursion issues. The important part is listed anyway.
Now that the tree has a link back to X, the traversal continues...
A
\
Y
/ \
(A) B
\
X
/ \
(Y) Z
/ \
C D
Then A is outputted, because it has no left child, and current is returned to Y, which was made A's right child in the previous iteration. On the next iteration, Y has both children. However, the dual-condition of the loop makes it stop when it reaches itself, which is an indication that it's left subtree has already been traversed. So, it prints itself, and continues with its right subtree, which is B.
B prints itself, and then current becomes X, which goes through the same checking process as Y did, also realizing that its left subtree has been traversed, continuing with the Z. The rest of the tree follows the same pattern.
No recursion is necessary, because instead of relying on backtracking through a stack, a link back to the root of the (sub)tree is moved to the point at which it would be accessed in a recursive inorder tree traversal algorithm anyway -- after its left subtree has finished.
The recursive in-order traversal is : (in-order(left)->key->in-order(right)). (this is similar to DFS)
When we do the DFS, we need to know where to backtrack to (that's why we normally keep a stack).
As we go through a parent node to which we will need to backtrack to -> we find the node which we will need to backtrack from and update its link to the parent node.
When we backtrack? When we cannot go further. When we cannot go further? When no left child's present.
Where we backtrack to? Notice: to SUCCESSOR!
So, as we follow nodes along left-child path, set the predecessor at each step to point to the current node. This way, the predecessors will have links to successors (a link for backtracking).
We follow left while we can until we need to backtrack. When we need to backtrack, we print the current node and follow the right link to the successor.
If we have just backtracked -> we need to follow the right child (we are done with left child).
How to tell whether we have just backtracked? Get the predecessor of the current node and check if it has a right link (to this node). If it has - than we followed it. remove the link to restore the tree.
If there was no left link => we did not backtrack and should proceed following left children.
Here's my Java code (Sorry, it is not C++)
public static <T> List<T> traverse(Node<T> bstRoot) {
Node<T> current = bstRoot;
List<T> result = new ArrayList<>();
Node<T> prev = null;
while (current != null) {
// 1. we backtracked here. follow the right link as we are done with left sub-tree (we do left, then right)
if (weBacktrackedTo(current)) {
assert prev != null;
// 1.1 clean the backtracking link we created before
prev.right = null;
// 1.2 output this node's key (we backtrack from left -> we are finished with left sub-tree. we need to print this node and go to right sub-tree: inOrder(left)->key->inOrder(right)
result.add(current.key);
// 1.15 move to the right sub-tree (as we are done with left sub-tree).
prev = current;
current = current.right;
}
// 2. we are still tracking -> going deep in the left
else {
// 15. reached sink (the leftmost element in current subtree) and need to backtrack
if (needToBacktrack(current)) {
// 15.1 return the leftmost element as it's the current min
result.add(current.key);
// 15.2 backtrack:
prev = current;
current = current.right;
}
// 4. can go deeper -> go as deep as we can (this is like dfs!)
else {
// 4.1 set backtracking link for future use (this is one of parents)
setBacktrackLinkTo(current);
// 4.2 go deeper
prev = current;
current = current.left;
}
}
}
return result;
}
private static <T> void setBacktrackLinkTo(Node<T> current) {
Node<T> predecessor = getPredecessor(current);
if (predecessor == null) return;
predecessor.right = current;
}
private static boolean needToBacktrack(Node current) {
return current.left == null;
}
private static <T> boolean weBacktrackedTo(Node<T> current) {
Node<T> predecessor = getPredecessor(current);
if (predecessor == null) return false;
return predecessor.right == current;
}
private static <T> Node<T> getPredecessor(Node<T> current) {
// predecessor of current is the rightmost element in left sub-tree
Node<T> result = current.left;
if (result == null) return null;
while(result.right != null
// this check is for the case when we have already found the predecessor and set the successor of it to point to current (through right link)
&& result.right != current) {
result = result.right;
}
return result;
}
I've made an animation for the algorithm here:
https://docs.google.com/presentation/d/11GWAeUN0ckP7yjHrQkIB0WT9ZUhDBSa-WR0VsPU38fg/edit?usp=sharing
This should hopefully help to understand. The blue circle is the cursor and each slide is an iteration of the outer while loop.
Here's code for morris traversal (I copied and modified it from geeks for geeks):
def MorrisTraversal(root):
# Set cursor to root of binary tree
cursor = root
while cursor is not None:
if cursor.left is None:
print(cursor.value)
cursor = cursor.right
else:
# Find the inorder predecessor of cursor
pre = cursor.left
while True:
if pre.right is None:
pre.right = cursor
cursor = cursor.left
break
if pre.right is cursor:
pre.right = None
cursor = cursor.right
break
pre = pre.right
#And now for some tests. Try "pip3 install binarytree" to get the needed package which will visually display random binary trees
import binarytree as b
for _ in range(10):
print()
print("Example #",_)
tree=b.tree()
print(tree)
MorrisTraversal(tree)
I found a very good pictorial explanation of Morris Traversal.
public static void morrisInOrder(Node root) {
Node cur = root;
Node pre;
while (cur!=null){
if (cur.left==null){
System.out.println(cur.value);
cur = cur.right; // move to next right node
}
else { // has a left subtree
pre = cur.left;
while (pre.right!=null){ // find rightmost
pre = pre.right;
}
pre.right = cur; // put cur after the pre node
Node temp = cur; // store cur node
cur = cur.left; // move cur to the top of the new tree
temp.left = null; // original cur left be null, avoid infinite loops
}
}
}
I think this code would be better to understand, just use a null to avoid infinite loops, don't have to use magic else. It can be easily modified to preorder.
I hope the pseudo-code below is more revealing:
node = root
while node != null
if node.left == null
visit the node
node = node.right
else
let pred_node be the inorder predecessor of node
if pred_node.right == null /* create threading in the binary tree */
pred_node.right = node
node = node.left
else /* remove threading from the binary tree */
pred_node.right = null
visit the node
node = node.right
Referring to the C++ code in the question, the inner while loop finds the in-order predecessor of the current node. In a standard binary tree, the right child of the predecessor must be null, while in the threaded version the right child must point to the current node. If the right child is null, it is set to the current node, effectively creating the threading, which is used as a returning point that would otherwise have to be on stored, usually on a stack. If the right child is not null, then the algorithm makes sure that the original tree is restored, and then continues traversal in the right subtree (in this case it is known that the left subtree was visited).
Python Solution
Time Complexity : O(n)
Space Complexity : O(1)
Excellent Morris Inorder Traversal Explanation
class Solution(object):
def inorderTraversal(self, current):
soln = []
while(current is not None): #This Means we have reached Right Most Node i.e end of LDR traversal
if(current.left is not None): #If Left Exists traverse Left First
pre = current.left #Goal is to find the node which will be just before the current node i.e predecessor of current node, let's say current is D in LDR goal is to find L here
while(pre.right is not None and pre.right != current ): #Find predecesor here
pre = pre.right
if(pre.right is None): #In this case predecessor is found , now link this predecessor to current so that there is a path and current is not lost
pre.right = current
current = current.left
else: #This means we have traverse all nodes left to current so in LDR traversal of L is done
soln.append(current.val)
pre.right = None #Remove the link tree restored to original here
current = current.right
else: #In LDR LD traversal is done move to R
soln.append(current.val)
current = current.right
return soln
PFB Explanation of Morris In-order Traversal.
public class TreeNode
{
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val = 0, TreeNode left = null, TreeNode right = null)
{
this.val = val;
this.left = left;
this.right = right;
}
}
class MorrisTraversal
{
public static IList<int> InOrderTraversal(TreeNode root)
{
IList<int> list = new List<int>();
var current = root;
while (current != null)
{
//When there exist no left subtree
if (current.left == null)
{
list.Add(current.val);
current = current.right;
}
else
{
//Get Inorder Predecessor
//In Order Predecessor is the node which will be printed before
//the current node when the tree is printed in inorder.
//Example:- {1,2,3,4} is inorder of the tree so inorder predecessor of 2 is node having value 1
var inOrderPredecessorNode = GetInorderPredecessor(current);
//If the current Predeccessor right is the current node it means is already printed.
//So we need to break the thread.
if (inOrderPredecessorNode.right != current)
{
inOrderPredecessorNode.right = null;
list.Add(current.val);
current = current.right;
}//Creating thread of the current node with in order predecessor.
else
{
inOrderPredecessorNode.right = current;
current = current.left;
}
}
}
return list;
}
private static TreeNode GetInorderPredecessor(TreeNode current)
{
var inOrderPredecessorNode = current.left;
//Finding Extreme right node of the left subtree
//inOrderPredecessorNode.right != current check is added to detect loop
while (inOrderPredecessorNode.right != null && inOrderPredecessorNode.right != current)
{
inOrderPredecessorNode = inOrderPredecessorNode.right;
}
return inOrderPredecessorNode;
}
}