Calculate Tree Height at each Node, help me explain this code solution - c++

Here's a code snippet of a solution that calculates height of each node in a binary tree and stores the height in each node. The code traverse the tree recursively, and below is the Node constructor.
class Node {
public:
int height; // to be set by computeHeight()
Node *left, *right;
Node() { height = -1; left = right = nullptr; }
~Node() {
delete left;
left = nullptr;
delete right;
right = nullptr;
}
};
Below is the function which computes and stores height at each Node. Where I am confused is how do leftHeight and rightHeight get updated by n->left->height and n->right->height if at construction, height is set to -1?
void computeHeight(Node *n) {
if (n == nullptr) {
return;
}
computeHeight(n->left);
computeHeight(n->right);
int leftHeight = -1;
int rightHeight = -1;
if (n->left != nullptr) {
leftHeight = n->left->height;
}
if (n->right != nullptr) {
rightHeight = n->right->height;
}
n->height = std::max(leftHeight, rightHeight) + 1;
}
Here is the main file that runs the function computeHeight
int main() {
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
n->right->right->right->left = new Node();
computeHeight(n);
delete n;
n = nullptr;
return 0;
}

Imagine a leaf node (left and right are nullptr). Then n->left != nullptr and n->right != nullptr are false so the calculation effectively becomes
int leftHeight = -1;
int rightHeight = -1;
n->height = std::max(leftHeight, rightHeight) + 1;
which is effectively
n->height = 0;
Now because of the way the recursion is done, each node gets it height calculated after it's children have had their heights calculated. So imagine a node with two children, each of which is a leaf node. We've already seen that leaf nodes get a height of zero. So the calculation for such a node is effectively
int leftHeight = -1;
int rightHeight = -1;
if (n->left != nullptr) {
leftHeight = 0; // because n->left is a leaf node
}
if (n->right != nullptr) {
rightHeight = 0; // because n->right is a leaf node
}
n->height = std::max(leftHeight, rightHeight) + 1;
which means that you end up with n->height = 1 for that node.
And so on. These calculations perculate up the tree, starting at the leaves, until finally the root gets it's height set.

We can solve this problem with a basic case of induction. Basically, let's start with the base case, and then assuming any case n works, we have to check if case n+1 works. In the case of calculating the heights of the nodes in a Binary Tree, the base case is when the root node is a leaf node, and for the n+1 case the left/right side nodes are the n cases. You can think of it as n is the height of the current node, and n=0 is the leaf node base case.
When the root node is a leaf, both the left and right side nodes are nullptr the method essentially turns into
void computeHeight(Node *n) {
int leftHeight = -1;
int rightHeight = -1;
n->height = std::max(leftHeight, rightHeight) + 1;
}
in which case n->height becomes 0, which is correct for a leaf node. Now, when the node is a non-leaf node, the statements
computeHeight(n->left);
computeHeight(n->right);
are already called before the calculation. This essentially makes it so that we assume that both the left and right side nodes are already taken care of, and their heights are correct. Then, we can use the left and right nodes' heights to calculate the root node's height, which is calculated through
int leftHeight = -1;
int rightHeight = -1;
if (n->left != nullptr) {
leftHeight = n->left->height;
}
if (n->right != nullptr) {
rightHeight = n->right->height;
}
n->height = std::max(leftHeight, rightHeight) + 1;
The trick here is that we already have called computeHeight() on the left and right side nodes so that when we do the calculations on the current node, we can safely assume that the child nodes have been totally taken care of. Also, the child nodes are calculated before the root node, so the program will first trickle all the way down to the leaves before coming back up and calculating the non-leaf nodes.

Related

Why do we return a pointer to a node instead of void function for avl tree implementation?

Okay so I seem to have gotten a bit lost. I am trying to insert data into a tree and when checking for balance and whether or not to rotate, I default to checking through the root. When I check examples online, I see that we can also rotate along other nodes in the tree as well. How do we figure out which node to use to balance the tree and how do we reach said node? I also saw that instead of having void functions to implement the insert and rotation functions, people use a node pointer return type instead. What is the purpose of that? I know the answers may be super obvious I am just lost.
struct TNode{
int data;
TNode* left;
TNode* right;
};
class Tree{
public:
TNode* root;
public:
Tree()
{root = nullptr;}
int height(TNode* node);
int balanceFactor(TNode* node);
bool isBalance(TNode* node)
{return balanceFactor(node)<-1 && balanceFactor(node)>1?true:false;}
void avlInsert(int key);
void LLRotation(TNode* node);
};
int Tree::height(TNode* node){
int l = 0;
int r = 0;
if(!node->left && !node->right)
return 0;
if(node->left)
l = height(node->left) + 1;
if(node->right)
r = height(node->right) + 1;
return l > r ? l : r;
}
int Tree::balanceFactor(TNode *node){
if(node->left && node->right)
return height(node->left) - height(node->right);
else if(node->left && !node->right)
return height(node);
else
return -1 * height(node);
}
void Tree::LLRotation(TNode *node){
TNode* nl = node->left;
node->left = nl->right;
nl->right = node;
if(root == node)
root = nl;
}
void Tree::avlInsert(int key){
TNode* trav;
TNode* follow;
TNode* node = new TNode;
node->data = key;
node->left = nullptr;
node->right = nullptr;
if (!root)
root = node;
else{
trav = root;
while (trav){
if (key < trav->data){
follow = trav;
trav = trav->left;
}
else{
follow = trav;
trav = trav->right;
}
}
if(key < follow->data)
follow->left = node;
else
follow->right = node;
}
if (balanceFactor(root) == 2 && balanceFactor(root->left) == 1)
LLRotation(root);
}
It really depends on your implementation some people they want to return a node, using a void function does not change the result much. For insert function you need to check only after you insert the node so you can go ahead and insert that node than rotate it. Please note that this function will only work if you have an AVL Tree to start with. Also your code is not correct, below:
if (balanceFactor(root) == 2 && balanceFactor(root->left) == 1)
LLRotation(root);
This is not how you check it for insertion.... That's for deletion buddy, I think you are getting mixed up. Usually for insertion you check if the balanceFactor is between 1 and -1, so if you are getting 2, then it is a left heavy tree, in your case, because it is left-right, which means you should do Right rotation........ Buddy......
int l = 0;
int r = 0;
if(!node->left && !node->right)
return 0;
if(node->left)
l = height(node->left) + 1;
if(node->right)
r = height(node->right) + 1;
return l > r ? l : r;
Height function, extremely inefficient... Instead define height as a variable for node and then modify it after each insertion, deletion.

Implementing a recursive Void function (Finding height of Binary Search Tree)

I need to implement a void function that computes the height of each node in a binary tree and stores it in each node. I've found a few solutions online that are recursive in nature but they return int. Examples include (https://www.geeksforgeeks.org/write-a-c-program-to-find-the-maximum-depth-or-height-of-a-tree/). The difference between the model answer, besides that it is not a void function, is that it also does not store the height in each node.
This is my attempt at the solution, but I can't seem to get the code to work, nor refit the model answer to recursively apply in a void function. When I run my code in the helper code to test, it doesn't even show any output.
void computeHeight(Node *n) {
Node* ltraverser = n;
Node* rtraverser = n;
int lheight = 0;
int rheight =0;
if (n == NULL) {
n->height = 0;
}
while (ltraverser->left != NULL) {
ltraverser = ltraverser->left;
lheight += 1;
}
while (rtraverser->right != NULL) {
rtraverser = rtraverser->right;
lheight += 1;
}
if (lheight > rheight) {
n->height = lheight;
}
else {
n->height = rheight;
}
computeHeight(n->left);
computeHeight(n->right);
}
For reference:
The starter code below defines a class called "Node" that has two child pointers ("left" , "right") and an integer "height" member variable. There is also a constructor Node() that initializes the children to nullptr and the height to -1.
/*
The height of a node is the number of edges in
its longest chain of descendants.
Implement computeHeight to compute the height
of the subtree rooted at the node n. Note that
this function does not return a value. You should
store the calculated height in that node's own
height member variable. Your function should also
do the same for EVERY node in the subtree rooted
at the current node. (This naturally lends itself
to a recursive solution!)
Assume that the following includes have already been
provided. You should not need any other includes
than these.
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
You have also the following class Node already defined.
You cannot change this class definition, so it is
shown here in a comment for your reference only:
class Node {
public:
int height; // to be set by computeHeight()
Node *left, *right;
Node() { height = -1; left = right = nullptr; }
~Node() {
delete left;
left = nullptr;
delete right;
right = nullptr;
}
};
*/
For testing the code
// This function prints the tree in a nested linear format.
void printTree(const Node *n) {
if (!n) return;
std::cout << n->height << "(";
printTree(n->left);
std::cout << ")(";
printTree(n->right);
std::cout << ")";
}
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
computeHeight(n);
printTree(n);
std::cout << std::endl << std::endl;
printTreeVertical(n);
delete n;
n = nullptr;
return 0;
}
Instead of returning node height just recurisvely call computeHeight on left and right nodes, then store maximum height in node structure.
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include <algorithm>
class Node {
public:
int height;
Node *left, *right;
Node() { height = -1; left = right = nullptr; }
~Node() {
delete left;
left = nullptr;
delete right;
right = nullptr;
}
};
void computeHeight(Node *node) {
if (node == nullptr) {
return;
}
computeHeight(node->left);
computeHeight(node->right);
int leftHeight = -1;
int rightHeight = -1;
if (node->left != nullptr) {
leftHeight = node->left->height;
}
if (node->right != nullptr) {
rightHeight = node->right->height;
}
node->height = std::max(leftHeight, rightHeight) + 1;
}
void printNode(Node *n, int level = 0) {
if (n == nullptr) {
return;
}
std::cout << std::string(level * 2, ' ') << "Height = " << n->height << "\n";
printNode(n->left, level + 1);
printNode(n->right, level + 1);
}
int main() {
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
computeHeight(n);
printNode(n);
}
Your mistake is on the following part and because of this you program exits without showing the error
if (n == NULL) {
n->height = 0;
}
When n is NULL; you should not try to access n->height. Replace it as follows and your code will work:
if (n == NULL) {
return;
}
Also, as the other answer mentioned, when you want to compute height recursively, you don't need a while loop just use the following recursive formula:
Height(n) = 1 + max(Height(n->left), Height(n->right))
Also, for consistency reasons usually the height of NULL subtree is defined to be -1. This allows the recursive formula to work properly.
Word of advice: In order to debug any program, an easy way is to just print messages before and after function calls and/or certain lines. This way by checking which messages are not printed, you can quickly pinpoint which functions/lines are causing a problem and then investigate them.

Accepting tree nodes from keyboard for determining it's height

I have a code that can determine tree height by hard coding it's values
I tried using container like structures but still was not successful, instead of posting what I have tried on the part of accepting tree nodes fro the Input which is actually messy,I decided to post the code with hard coded tree nodes, what I need is for the program to accept tree nodes from the keyboard with the following helper description for input
Input:
The first line is an integer N indicating the number of nodes.
For each of the next few lines, there are two integers include a and b.b is a child of a.
example:
5 // number of nodes
1 2
1 3
3 4
3 5
in which the height will be 3
// C++ program to find height of tree
#include <bits/stdc++.h>
using namespace std;
/* A binary tree node has data, pointer to left child
and a pointer to right child */
class node
{
public:
int data;
node* left;
node* right;
};
/* Compute the "maxDepth" of a tree -- the number of
nodes along the longest path from the root node
down to the farthest leaf node.*/
int maxDepth(node* node)
{
if (node == NULL)
return 0;
else
{
/* compute the depth of each subtree */
int lDepth = maxDepth(node->left);
int rDepth = maxDepth(node->right);
/* use the larger one */
if (lDepth > rDepth)
return(lDepth + 1);
else return(rDepth + 1);
}
}
/* Helper function that allocates a new node with the
given data and NULL left and right pointers. */
node* newNode(int data)
{
node* Node = new node();
Node->data = data;
Node->left = NULL;
Node->right = NULL;
return(Node);
}
// Driver code
int main()
{
node *root = newNode(1);
root->left = newNode(2);
root->right = newNode(3);
root->left->left = newNode(4);
root->left->right = newNode(5);
cout << "Height of tree is " << maxDepth(root);
return 0;
}
Since the input identifies the parent node by its data value, we need a helper function to find it:
node *findNode(node *node, int data)
{
if (!node) return 0;
if (node->data == data) return node;
class node *found;
(found = findNode(node->left, data)) || (found = findNode(node->right, data));
return found;
}
Then we can code the input processing, e. g.:
node *node, *root = 0; // initially empty
int nn, a, b;
cin>>nn;
while (cin>>a>>b)
{
if (!root)
root = newNode(a),
node = root;
else
node = findNode(root, a);
if (!node->left) node->left = newNode(b);
else node->right = newNode(b);
}

Finding the deepest level left leaf node in a binary search tree

Given a Binary Tree, find the deepest leaf node that is left child of its parent. For example, consider the following tree. The deepest left leaf node is the node with value 9.
1
/ \
2 3
/ / \
4 5 6
\ \
7 8
/ \
9 10
The answer is 9.
I developed the following code for this:
int maxlevel = 0;
Node *newNode(int data)
{
Node *temp = new Node;
temp->val = data;
temp->left = temp->right = NULL;
return temp;
}
Node * root;
Node * maxi = NULL;
int getlevel (Node * treeroot,int level, Node * foo)
{
if (treeroot == NULL)
return -1;
else if (treeroot->val == foo->val)
return level+1;
else
{
int downlevel = getlevel(treeroot->left,level+1,foo);
if (downlevel != -1)
return downlevel;
else
downlevel = getlevel(treeroot->right,level+1,foo);
return downlevel;
}
}
void foo(Node * temp)
{
// Base case
if (temp == NULL)
return;
Node * prev;
if (temp->left != NULL)
{
prev = temp;
foo(temp->left);
}
if (prev->left != NULL)
{
if (temp->left == NULL && temp->right == NULL && prev->left == temp)
{
int ind = getlevel(root,0,temp);
if (ind > maxlevel)
{
maxlevel = ind;
maxi = temp;
}
}
}
foo(temp->right);
return;
}
Here, foo is the actual function which determines the deepest left leaf in a tree. getlevel is a function which gets the level of a node in a tree. newNode is a function which allocates a new node.
When I try giving this input tree, it says the leaf is not present. Is there something wrong with my logic?
Thanks!
I think you could simplify this a little by passing the current level and a flag indicating whether the current node is a left or right child into each call...
Something like this:
int maxLevel = 0;
Node* maxNode = null;
void findDeepestLeftNode(Node* node, int level, bool isLeftChild) {
bool isLeaf = true;
if (node->left != null) {
isLeaf = false;
findDeepestLeftNode(node->left, level + 1, true);
}
if (node->right != null) {
isLeaf = false;
findDeepestLeftNode(node->right, level + 1, false);
}
if (isLeaf && isLeftChild && level > maxLevel) {
maxLevel = level;
maxNode = node;
}
}
Then just call it with:
findDeepestLeftNode(root, 0, false);
Assumes the root is not null.

AVL TREE - Most efficient way to print a value, by location. C++

Hey I have to find the most eficient way to print a number by giving the postion. The input is like this:
8 (N-> N Numbers)
INS 100 (Add 100 to the tree)
INS 200 (Add 200 to the tree)
INS 300 (Add 300 to the tree)
REM 200 (Remove the number 200 from the tree)
PER 1 (Have to output the biggest number in the tree-> Shoud print 300)
INS 1000 (Add 1000 to the tree)
PER 1 ((Have to output the biggest number in the tree-> Shoud print 1000))
PER 2 (I have to output the second biggest number so: 300)
I have a way to print like this, but is very slow and I have to maintain a O(N * log(N)).
Here is my full code
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
// An AVL tree node
struct node
{
int key;
struct node *left;
struct node *right;
int height;
};
// A utility function to get maximum of two integers
int max(int a, int b);
// A utility function to get height of the tree
int height(struct node *N)
{
if (N == NULL)
return 0;
return N->height;
}
// A utility function to get maximum of two integers
int max(int a, int b)
{
return (a > b)? a : b;
}
/* Helper function that allocates a new node with the given key and
NULL left and right pointers. */
struct node* newNode(int key)
{
struct node* node = (struct node*)
malloc(sizeof(struct node));
node->key = key;
node->left = NULL;
node->right = NULL;
node->height = 1; // new node is initially added at leaf
return(node);
}
// A utility function to right rotate subtree rooted with y
// See the diagram given above.
struct node *rightRotate(struct node *y)
{
struct node *x = y->left;
struct node *T2 = x->right;
// Perform rotation
x->right = y;
y->left = T2;
// Update heights
y->height = max(height(y->left), height(y->right))+1;
x->height = max(height(x->left), height(x->right))+1;
// Return new root
return x;
}
// A utility function to left rotate subtree rooted with x
// See the diagram given above.
struct node *leftRotate(struct node *x)
{
struct node *y = x->right;
struct node *T2 = y->left;
// Perform rotation
y->left = x;
x->right = T2;
// Update heights
x->height = max(height(x->left), height(x->right))+1;
y->height = max(height(y->left), height(y->right))+1;
// Return new root
return y;
}
// Get Balance factor of node N
int getBalance(struct node *N)
{
if (N == NULL)
return 0;
return height(N->left) - height(N->right);
}
struct node* insert(struct node* node, int key)
{
/* 1. Perform the normal BST rotation */
if (node == NULL)
return(newNode(key));
if (key < node->key)
node->left = insert(node->left, key);
else
node->right = insert(node->right, key);
/* 2. Update height of this ancestor node */
node->height = max(height(node->left), height(node->right)) + 1;
/* 3. Get the balance factor of this ancestor node to check whether
this node became unbalanced */
int balance = getBalance(node);
// If this node becomes unbalanced, then there are 4 cases
// Left Left Case
if (balance > 1 && key < node->left->key)
return rightRotate(node);
// Right Right Case
if (balance < -1 && key > node->right->key)
return leftRotate(node);
// Left Right Case
if (balance > 1 && key > node->left->key)
{
node->left = leftRotate(node->left);
return rightRotate(node);
}
// Right Left Case
if (balance < -1 && key < node->right->key)
{
node->right = rightRotate(node->right);
return leftRotate(node);
}
/* return the (unchanged) node pointer */
return node;
}
/* Given a non-empty binary search tree, return the node with minimum
key value found in that tree. Note that the entire tree does not
need to be searched. */
struct node * minValueNode(struct node* node)
{
struct node* current = node;
/* loop down to find the leftmost leaf */
while (current->left != NULL)
current = current->left;
return current;
}
struct node* apagaNode(struct node* root, int key)
{
// STEP 1: PERFORM STANDARD BST DELETE
if (root == NULL)
return root;
// If the key to be deleted is smaller than the root's key,
// then it lies in left subtree
if ( key < root->key )
root->left = apagaNode(root->left, key);
// If the key to be deleted is greater than the root's key,
// then it lies in right subtree
else if( key > root->key )
root->right = apagaNode(root->right, key);
// if key is same as root's key, then This is the node
// to be deleted
else
{
// node with only one child or no child
if( (root->left == NULL) || (root->right == NULL) )
{
struct node *temp = root->left ? root->left : root->right;
// No child case
if(temp == NULL)
{
temp = root;
root = NULL;
}
else // One child case
*root = *temp; // Copy the contents of the non-empty child
free(temp);
}
else
{
// node with two children: Get the inorder successor (smallest
// in the right subtree)
struct node* temp = minValueNode(root->right);
// Copy the inorder successor's data to this node
root->key = temp->key;
// Delete the inorder successor
root->right = apagaNode(root->right, temp->key);
}
}
// If the tree had only one node then return
if (root == NULL)
return root;
// STEP 2: UPDATE HEIGHT OF THE CURRENT NODE
root->height = max(height(root->left), height(root->right)) + 1;
// STEP 3: GET THE BALANCE FACTOR OF THIS NODE (to check whether
// this node became unbalanced)
int balance = getBalance(root);
// If this node becomes unbalanced, then there are 4 cases
// Left Left Case
if (balance > 1 && getBalance(root->left) >= 0)
return rightRotate(root);
// Left Right Case
if (balance > 1 && getBalance(root->left) < 0)
{
root->left = leftRotate(root->left);
return rightRotate(root);
}
// Right Right Case
if (balance < -1 && getBalance(root->right) <= 0)
return leftRotate(root);
// Right Left Case
if (balance < -1 && getBalance(root->right) > 0)
{
root->right = rightRotate(root->right);
return leftRotate(root);
}
return root;
}
int imprime(struct node *root,int targetPos,int curPos)
{
if(root != NULL)
{
int newPos = imprime(root->left, targetPos, curPos);
newPos++;
if (newPos == targetPos)
{
printf("%d\n", root->key);
}
return imprime(root->right, targetPos, newPos);
}
else
{
return curPos;
}
}
int main()
{
struct node *root = NULL;
int total=0;
int n,b;
string a;
cin >> n;
for (int i=0; i<n; i++)
{
cin >> a >> b;
if(a=="INS")
{root = insert(root, b);total=total+1;}
else
if(a=="REM")
{root = apagaNode(root, b);total=total-1;}
else
imprime(root, total-b+1, 0);
}
return 0;
}
The way I found to print the values:
int imprime(struct node *root,int targetPos,int curPos)
{
if(root != NULL)
{
int newPos = imprime(root->left, targetPos, curPos);
newPos++;
if (newPos == targetPos)
{
printf("%d\n", root->key);
}
return imprime(root->right, targetPos, newPos);
}
else
{
return curPos;
}
}
Problem is that this function is very slow, and I can't use it. How is the best way to print by a given postion like this? (A heard about, counting n_nodes, and during the rotations I have to incremennt, decrement, i realy did not understand. Help me please! Give some tips, and advices) (PS: I'm not an expert with this kind of algorithms)
The advice you heard is correct: you should add a node counter to your node structure:
struct node
{
int key;
struct node *left;
struct node *right;
int height;
int n_nodes;
};
It should hold the number of nodes in the tree. Assuming it's correct, you can improve the algorithm for finding a node with a target position: it will know exactly in which branch of the tree to look (left or right), which will make the search faster (current imprime implementation is O(n)).
So, how to make it so the n_nodes field holds the right value? Fortunately, you already have an example: height. Look where your existing code changes it; these are roughly the places where you have to update n_nodes, too. Most of them are trivial (just add 1 to it); the more interesting ones are the rotation functions:
struct node *rightRotate(struct node *y)
{
struct node *x = y->left;
struct node *T2 = x->right;
// Perform rotation
x->right = y;
y->left = T2;
// Update heights
y->height = max(height(y->left), height(y->right))+1;
x->height = max(height(x->left), height(x->right))+1;
// Update numbers of nodes
x->n_nodes = ...;
y->n_nodes = ...;
T2->n_nodes = ...;
// Return new root
return x;
}
So it transforms the tree like this:
y x
/ \ / \
x D A y
/ \ ==> / \
A T2 T2 D
/ \ / \
B C B C
Here A, B, C and D are trees whose sizes your program knows; let's denote their sizes as a, b, c and d. So the transformation changes these sizes like this:
size of x: from a+b+c+2 to a+b+c+d+3
size of y: from a+b+c+d+3 to b+c+d+2
size of T2: unchanged
So just transform this to code.