RTE in the bst program - recursion

I have to check if this is BST or not i have implemented recursion and it is giving run time error for the tree level order 2 N 7 N 6 N 5 N 9 N 2 N 6
bool isBST(Node* root)
{
if(root==NULL)
return 1;
if(root->left==NULL && root->right==NULL)
return 1;
if((root->data)<(root->left->data))
return 0;
if( (root->data)>(root->right->data))
return 0;
if(root->right==NULL)
return isBST(root->left);
if(root->left==NULL)
return isBST(root->right);
if(!isBST(root->left) || !isBST(root->right))
{
return 0;
}
return 1;
}

Your code may dereference a null pointer. For instance, if root has a right child, but not a left child, then this code will be executed which performs an invalid dereference:
if((root->data)<(root->left->data))
But even if you add the necessary checks to avoid such invalid dereferencing, the algorithm is not correct.
It is not true that the following conditions define a valid BST:
The left child of a node is either null or has a value that is not greater than the node's own value, AND
The right child of a node is either null or has a value that is not less than the node's own value, AND
The above is also true for the left and right child (recursive step)
For instance, this would all be true for this tree:
5
/ \
2 8
\
7
... but this tree is not a valid BST, because 7 is greater than 5, which is not allowed. All values in the left subtree of a node must not be greater than the node's own value. Your code only checks this for the direct child, but it should make sure that this is also true for any other descendants in that left subtree.
The common way to make a correct verification, is to pass to the recursive call a window (minimum and maximum) of values which the subtree may contain.

Related

Time complexity of returning every path in a binary tree whose path equals the target sum

I solved LeetCode challenge 113. Path Sum II:
Given the root of a binary tree and an integer targetSum, return all root-to-leaf paths where the sum of the node values in the path equals targetSum. Each path should be returned as a list of the node values, not node references.
A root-to-leaf path is a path starting from the root and ending at any leaf node. A leaf is a node with no children.
This is my code:
var pathSum = function(root, targetSum) {
let paths = [];
if(root === null) {
return [];
}
getAllSumPaths(root, [], paths, targetSum);
return paths;
};
function getAllSumPaths(root, currPath, paths, targetSum) {
if(root.left === null && root.right === null) {
if(targetSum - root.val === 0) {
currPath.push(root.val);
paths.push([...currPath]);
currPath.pop();
}
return;
}
currPath.push(root.val);
if(root.left !== null){
getAllSumPaths(root.left, currPath, paths, targetSum - root.val);
}
if(root.right !== null){
getAllSumPaths(root.right, currPath, paths, targetSum - root.val);
}
currPath.pop();
}
Initially I figured the time complexity would just be O(n) where n is the number of nodes in the tree. However, while writing this, I had to use the spread operator to create a new instance of a valid path to add to my paths array by doing paths.push([...currPath]); because the subsequent pop() calls would modify the paths already pushed and I would get empty paths in the end.
But I found that the spread operation has an O(n) time complexity where n would be the size of the path. I'm not sure how that factors into the time complexity of the algorithm. Any ideas? and is there another way to write this so I don't have an O(n) operation when I find a valid path?
I had to use the spread operator to create a new instance of a valid path to add to my paths array by doing paths.push([...currPath])
Yes, this is needed, as otherwise you only have one path that keeps on mutating.
the spread operation has an O(n) time complexity where n would be the size of the path. I'm not sure how that factors into the time complexity of the algorithm.
You are right that the 𝑛 in that O(𝑛) is limited to the size of the path, which is limited by the height of the tree. On average the height of a tree is O(log𝑛) where 𝑛 is the number of nodes in the tree, so that spread operation has an average time complexity of O(log𝑛).
Since the challenge description says that node values can be both positive and negative, there may also be cases where a suitable path can be extended to another suitable path (by adding a total value of 0).
is there another way to write this so I don't have an O(n) operation when I find a valid path?
No. The expected output has to include all paths, and since these paths are all distinct they each occupy distinct memory for the node references they contain.
Take for example this tree, and with 5 as the required sum
0
/ \
5 1
/ \ / \
-2 -1 4 2
/ / / /
2 1 0 2
Then the expected output would (in any order) be:
[[0,5],[0,5,-2,2],[0,5,-1,1],[5],[5,-2,2],[5,-1,1],[0,1,4],[0,1,2,2],[1,4],[1,2,2]]
...where the numbers are actually node instances.
Note that there are 29 nodes in that output, while the tree only has 11 nodes. Many nodes appear more than once, because they are part of different paths.
The time complexity is thus directly related to the size of the output.
The very worst case is an input of a perfect binary tree, where each node value is 0 and the expected sum is 0. That means that all possible (downward) paths (without any restriction caused by the sum) should be included in the output:
0
/ \
0 0
/ \ / \
0 0 0 0
/\ /\ /\ /\
0 0 0 0 0 0 0 0
The paths can be categorised by their length:
There are 𝑛 paths of length 1
There are 𝑛−1 paths of length 2
There are 𝑛−3 paths of length 3
...
There are 𝑛−(2𝑘−1) paths of length 𝑘
(the above tree has 𝑛 + 𝑛−1 + 𝑛−3 + 𝑛−7 = 4𝑛−11 = 49 paths)
So ∑𝑘=0log𝑛 (𝑛−(2𝑘−1))
= ∑𝑘=0log𝑛 (𝑛+1−2𝑘)
Let's split the summation into two sums to take that subtraction apart:
= (𝑛+1)log𝑛 − ∑𝑘=0log𝑛 2𝑘
This summation is a geometric series
= (𝑛+1)log𝑛 − 2log𝑛−1
= (𝑛+1)log𝑛 − 𝑛/2
= O(𝑛log𝑛)

How does null move up the call stack in recursive functions, if a return value of null is not specified?

Hi I'm currently learning about recursive Inorder Binary Tree Traversal using C#. There's one main aspect I cannot understand, in particular with this code below.
public void InOrder(BinaryTreeNode node)
{
if (node != null)
{
InOrder(node.Left);
Console.WriteLine(node.Value);
InOrder(node.Right);
}
}
If I had a Binary tree that looked like this...
9
/ \
4 20
/ \ / \
1 6 15 170
I know that eventually by recursively calling Inorder(node.left) I will get to the left leaf of the binary tree i.e. the very end of the tree, where node.left will equal null as there are no more nodes.
The tree would look like this...
9
/ \
4 20
/ \ / \
1 6 15 170
/
null
Because node.left = null, the first recursive function
InOrder(node.left)
will terminate, and
Console.Writeline(node.left)
will execute
Printing a value of 1
Eventually these null values move up the call stack after each node is analysed, and all nodes are printed, the tree starts to look like this, as null value moves up the tree..
9
/ \
4 20
/ \ / \
null 6 15 170
/ \ / \
null null null
Eventually all the nodes in the tree are equal to null, and all nodes are printed in order to an output of ...
1, 4, 6, 9, 15, 20, 170
What I don't understand is how this null value is moving up the tree, and changing all the nodes that have been analysed to null when there is no return value. Normally there would be a base case like...
if (node == null)
{
return null;
}
For this, I understand that null is being returned so will persist/return up the call stack. But for fist block of code above, there is no return statement.
I also find it just as confusing when there is only a return statement without a return value like...
if (node == null)
{
return;
}
Again there is no return of null specified, so how does this null value move up the tree as each node is evaluated?
There isn't a problem with any of this code, it works as expected, and prints all the nodes of the Binary Tree InOrder. This is more about understanding Recursion, and why the first block of code still works even though a return null value is not specified.
Thanks in Advance for the help.
there is no return of null specified, so how does this null value move up the tree as each node is evaluated?
The function will still return, even if there is no value to return. It's done executing, so control is passed back to the caller.
if (node != null) <- skipped entirely when the node is null
{
InOrder(node.Left);
Console.WriteLine(node.Value);
InOrder(node.Right);
}
For the tree you gave, this is what happens at the node with value=1:
It's not null, so we go into the if block.
We evaluate InOrder(node.Left) which is just InOrder(null):
It's null, so the if block is skipped.
We return to the caller, InOrder(node with value=1)
Console.WriteLine(node.Value) prints 1.
etc...
Although you can't 'see' the base case in the code, it's still there :) just implicitly.

Finding the minimum node and then returning the next minimum natural value that's not in the BST

So I have a problem and I have the Algorithm for it, but I just can't seem to be able to turn it into code in C.
Problem: Given an AVL tree, return the next minimum natural value that's not in the tree.
Example: if 2 is the minimum in the tree, I should find out whether or not 3 is one of the nodes in the tree, if it is not, I should return the value 3, if it is, I should see if 4 is in the tree, and so on...
Algorithm to the problem that works in O(logn) (when n is the Number of nodes found in the tree):
first, we check if node->size = node -> key - TreeMinimum
if yes, go to the right side of the tree.
if no, then go to the left.
when we reach NULL, we should return the value of the last node we visited plus 1.
SIZE of the node is the number of nodes that are under this node, including the node itself.
I wrote this code in c but it doesn't seem to work :
int next_missing( AVLNodePtr tnode )
{
int x,y;
if(tnode==NULL)
{
return (tnode->key)+1;
}
if(tnode->size == tnode->key - FindMin(tnode))
x = next_missing(tnode->child[1]);
if(tnode->size != tnode->key - FindMin(tnode))
y = next_missing(tnode->child[0]);
if(x>y) return y;
else return x;
}
Any help/tips on how to fix the code would be appreciated.
Thanks.

Recursion and a counter variable in a binary tree

Following are 2 codes:
1. Find the kth smallest integer in a binary search tree:
void FindKthSmallest(struct TreeNode* root, int& k)
{
if (root == NULL) return;
if (k == 0) return; // k==0 means target node has been found
FindKthSmallest (root->left, k);
if (k > 0) // k==0 means target node has been found
{
k--;
if (k == 0) { // target node is current node
cout << root->data;
return;
} else {
FindKthSmallest (root->right, k);
}
}
}
Find the number of nodes in a binary tree:
int Size (struct TreeNode* root)
{
if (root == NULL) return 0;
int l = Size (root->left);
int r = Size (root->right);
return (l+r+1);
}
My Question:
In both these codes, I will have to keep track of the number of nodes I visit. Why is it that code 1 requires passing a parameter by reference to keep track of the number of nodes I visit, whereas code 2 does not require any variable to be passed by reference ?
The first code (1) is looking for the smallest node in your BST. You search from the root down the left side of the tree since the smallest valued node will be found in that location. You make several checks:
root == null - to determine if the tree is empty.
k == 0 - zero in this case is the smallest element. You are making this assumption based on whatever principles are apart of this tree.
Then you recursively traverse the list to find the next smallest in the left side of the tree. You perform one more check that if k > 0 you decrement k <- this is why you need to pass by reference since you are making changes to some value k given by a separate function, global variable, etc. If k happens to be zero then you have found the smallest valued node, if not you go one right of the current node and then continue the process from there. This seems like a very arbitrary way of finding the smallest node...
For the second code (2) you are just counting the nodes in your tree starting at the root and counting each subsequent node (either left or right) recursively until no more nodes can be found. You return your result which is the total amount of left nodes,right nodes. and + 1 for the root since it was not counted earlier. In this instance no passed by reference variable is needed although you could potentially implement one if you choose to do so.
Does this help?
Passing the parameter by reference allows you to keep track of the count within the recursive process, otherwise the count would reset. It allows you to modify the data within the memory space, thus changing the former value not the current/local value.

Recursion in Tree

I was trying to understand program of recursion
Anyone Please explain working of size(). how it is returning no. of nodes recursively.
int size(struct tree *root)
{
if (root==NULL)
return 0;
else
{
return (size(root->left)+size(root->right)+1);
}
}:
In this program what does size(root->left),size(root->right) will return??
As in factorial program
function factorial (x)
{
return (x * factorial(x-1) ) ;
}
In this factorial program it will return 4*3*2*1.If we calculate for factorial(4).
In the above tree program what should return value of that node.Why it is returning no. of nodes?not the value of that node.
Please Explain.
The size function is calculating the number of nodes in the tree (completely independent of the values of the nodes). The recursion works because if the tree root is NULL it returns 0 (base case). If the root is not NULL, it has a left and right child (both of which are trees). So the total size will be size of left subtree (i.e. size(root->left)) + size of right subtree (i.e .size(root->right)) + size of root node (i.e. 1).
your program never is reading the value of node. instead is counting no. of nodes.
it returns 0 on reaching null. it adds 1 when all the nodes in right and left subtree are counted and returns the final sum.

Resources