Number of Leaf Nodes in a Binary Tree at a given Level? - data-structures

Given a Binary Tree, how can we find the number of Leaf nodes at a particular level, considering the level of the root is 1 and so on.

You can simply use BFS or DFS algorithm. Something like that (in pseudocode):
Node_counter(root, N):
1. IF root is null or N<1 return 0
2. IF N==1
2.1 if root is leaf return 1
2.2 otherwise return 0
3. Otherwise return Node_counter(root->left, N-1)+Node_counter(root->right, N-1)
Complexity is O(N)

private int noOfleafLevel(Node root, int leaflevel) {
if(root==null)
return 0;
if(root.left==null&&root.right==null&&leaflevel==1)
return 1;
else
return noOfleafLevel(root.left, leaflevel - 1)+noOfleafLevel(root.right, leaflevel - 1);
}
This is the code for getting Leaf at a particular level, using level order traversal.

Related

Longest path for each node to a leaf in a BST

For each node in a BST, what is the length of the longest path from the node to a leaf? (worst case)
I think in the worst case we have a linear path from a node to a leaf. If there are n nodes in a tree, then the running time is O(n*n). Is this right?
You can do this in linear time, assuming it's "A given node to every leaf" or "Every node to a given leaf." If it's "Every Node to Every Leaf", that's a bit harder.
To do this: Walk from the "target" to the root, marking each node by distance; colors all of these nodes red. (So, the root holds the depth of the target, and the target holds 0). For each red node, walk its non-red children, adding 1 as you descend, starting from the value of the red node.
It's not O(n*n) because you're able to re-use a lot of your work; you don't find one path, then start completely over to find the next.
The longest path from a node to a leaf would be
1. Going all the way from the node to the root
2.Then going from the root down to the deepest leaf
3.Things to ensure would be not to traverse a node twice because if thus was allowed, the path could be made infinitely long by going between any two nodes multiple times
x
/ \
b a
\
c
\
d
The longest path from c to leaf would do two things
1. Go from c to x (count this length)
2. Go from x to deepest leaf which does not have c in its path (in this case that leaf is b)
The code below has a time complexity of O(n) for finding longest distance from a single node
Therefore for finding distance for all nodes would be O(n^2)
public int longestPath(Node n, Node root) {
int path = 0;
Node x = n;
while (x.parent != null) {
x = x.parent;
path++;
}
int height = maxHeight(root, n);
return path + height;
}
private int maxHeight(Node x, Node exclude) {
if (x == exclude)
return -1;
if (x == null)
return -1;
if (x.left == null && x.right == null)
return 0;
int l = maxHeight(x.left, exclude);
int r = maxHeight(x.right, exclude);
return l > r ? l + 1 : r + 1;
}
My solution would be.
1) Check if the node has subtree, if yes then find the height of that subtree.
2) Find the height of rest of the tree, as mentioned by #abhaybhatia
return the max of two heights

Runtime complexity of brute-force for determining balanced binary tree

I have the following codes for the brute-force method to determine whether a binary tree is balanced or not:
public boolean IsBalanced(Node root)
{
if (root == null) return true;
return Math.abs(maxDepth(root.left) - maxDepth(root.right)) <= 1
&& IsBalanced(root.left)
&& IsBalanced(root.right)
}
public int maxDepth(Node root)
{
if (root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
I don't understand why the worst case runtime complexity is O(n^2) when the tree is a skewed tree. What I think is that if the tree is skewed, then the line
Math.abs(maxDepth(root.left) - maxDepth(root.right)) <= 1
would immediately find that the height of the left subtree of the root is over 1 more than the height of the root's right subtree. Then the time complexity of the skewed tree case should be O(n). What am I missing here? Thanks!
In the method IsBalanced(Node root) for a skewed tree when it first calls
maxDepth(root.left) it takes n recursive calls in maxDepth(root) now still the
root is not null in IsBalanced(Node root) then again it calls
maxDepth(root.left) now for n-1 times and so on.so the time complexity is sum of
first n natural numbers i.e O(n^2).

Time Complexity for Finding the Minimum Value of a Binary Tree

I wrote a recursive function for finding the min value of a binary tree (assume that it is not ordered).
The code is as below.
//assume node values are positive int.
int minValue (Node n) {
if(n == null) return 0;
leftmin = minValue(n.left);
rightmin = minValue(n.right);
return min(n.data, leftmin, rightmin);
}
int min (int a, int b, int c) {
int min = 0;
if(b != 0 && c != 0) {
if(a<=b) min =a;
else min =b;
if(min<=c) return min;
else return c;
}
if(b==0) {
if(a<=c) return a;
else return c;
}
if(c==0) {
if(a<=b) return a;
else return b;
}
}
I guess the time complexity of the minValue function is O(n) by intuition.
Is this correct? Can someone show the formal proof of the time complexity of minValue function?
Assuming your binary tree is not ordered, then your search algorithm will have O(N) running time, so your intuition is correct. The reason it will take O(N) is that you will, on average, have to search half the nodes in the tree to find an input. But this assumes that the tree is completely unordered.
For a sorted and balanced binary tree, searching will take O(logN). The reason for this is that the search will only ever have to traverse one single path down the tree. A balanced tree with N nodes will have a height of log(N), and this explains the complexity for searching. Consider the following tree for example:
5
/ \
3 7
/ \ / \
1 4 6 8
There are 8 (actually 7) nodes in the tree, but the height is only log(8) = 2. You can convince yourself that you will only ever have to traverse this tree once to find a value or fail doing so.
Note that for a binary tree which is not balanced these complexities may not apply.
The number of comparisons is n-1. The proof is an old chestnut, usually applied to the problem of saying how many matches are needed in a single-elimination tennis match. Each comparison removes exactly one number from consideration and so if there's initially n numbers in the tree, you need n-1 comparisons to reduce that to 1.
You can lookup and remove the min/max of a BST in constant time O(1), if you implement it yourself and store a reference to head/tail. Most implementations don't do that, only storing the root-node. But if you analyze how a BST works, given a ref to min/max (or aliased as head/tail), then you can find the next min/max in constant time.
See this for more info:
https://stackoverflow.com/a/74905762/1223975

What's wrong with the most cited binary tree depth calculation algorithm?

There is something that eats into my brain: how can the depth of the following tree
b
/ \
a c
be 3, after the most cited algorithm (here in Java):
int depth(Node n)
{
if(n == null)
{
return 0;
}
int lDepth = depth(n.left);
int rDepth = depth(n.right);
return 1 + ((lDepth > rDepth) ? lDepth : rDepth);
}
when the depth of a tree with only a single (root) node is 0 according to Wikipedia and many of my other sources where the depth is defined as length of path to the deepest node? Obviously, the length of the path to the deepest node for a tree with only a single node is 0, while the above algorithm will never yield anything smaller than 1.
Is the depth of a tree with a single root node 0 or is it 1? If it is 0 then the algorithm above is faulty, because it will yield 1.
I never thought such a trivial thing would turn inside out on me.
The height of a tree is usually defined as:
1. The number of edges on the longest path from the root to a leaf
or
2. The number of nodes on the longest path from the root to a leaf
Of course you can easily transform a result given by any of the above definitions to a result of the other - just subtract/add 1.
You are correct, that the first definition is used more frequently.
As you can see, the algorithm which you are according to, follows the second definition.

How to finding first common ancestor of a node in a binary tree?

Following is my algorithm to find first common ancestor. But I don’t know how to calculate it time complexity, can anyone help?
public Tree commonAncestor(Tree root, Tree p, Tree q) {
if (covers(root.left, p) && covers(root.left, q))
return commonAncestor(root.left, p, q);
if (covers(root.right, p) && covers(root.right, q))
return commonAncestor(root.right, p, q);
return root;
}
private boolean covers(Tree root, Tree p) { /* is p a child of root? */
if (root == null) return false;
if (root == p) return true;
return covers(root.left, p) || covers(root.right, p);
}
Ok, so let's start by identifying what the worst case for this algorithm would be. covers searches the tree from left to right, so you get the worst-case behavior if the node you are searching for is the rightmost leaf, or it is not in the subtree at all. At this point you will have visited all the nodes in the subtree, so covers is O(n), where n is the number of nodes in the tree.
Similarly, commonAncestor exhibits worst-case behavior when the first common ancestor of p and q is deep down to the right in the tree. In this case, it will first call covers twice, getting the worst time behavior in both cases. It will then call itself again on the right subtree, which in the case of a balanced tree is of size n/2.
Assuming the tree is balanced, we can describe the run time by the recurrence relation T(n) = T(n/2) + O(n). Using the master theorem, we get the answer T(n) = O(n) for a balanced tree.
Now, if the tree is not balanced, we might in the worst case only reduce the size of the subtree by 1 for each recursive call, yielding the recurrence T(n) = T(n-1) + O(n). The solution to this recurrence is T(n) = O(n^2).
You can do better than this, though.
For example, instead of simply determining which subtree contains p or q with cover, let's determine the entire path to p and q. This takes O(n) just like cover, we're just keeping more information. Now, traverse those paths in parallell and stop where they diverge. This is always O(n).
If you have pointers from each node to their parent you can even improve on this by generating the paths "bottom-up", giving you O(log n) for a balanced tree.
Note that this is a space-time tradeoff, as while your code takes O(1) space, this algorithm takes O(log n) space for a balanced tree, and O(n) space in general.
As hammar’s answer demonstrates, your algorithm is quite inefficient as many operations are repeated.
I would do a different approach: Instead of testing for every potential root node if the two given nodes are not in the same sub-tree (thus making it the first common ancestor) I would determine the the paths from the root to the two given nodes and compare the nodes. The last common node on the paths from the root downwards is then also the first common ancestor.
Here’s an (untested) implementation in Java:
private List<Tree> pathToNode(Tree root, Tree node) {
List<Tree> path = new LinkedList<Tree>(), tmp;
// root is wanted node
if (root == node) return path;
// check if left child of root is wanted node
if (root.left == node) {
path.add(node);
path.add(root.left);
return path;
}
// check if right child of root is wanted node
if (root.right == node) {
path.add(node);
path.add(root.right);
return path;
}
// find path to node in left sub-tree
tmp = pathToNode(root.left, node);
if (tmp != null && tmp.size() > 1) {
// path to node found; add result of recursion to current path
path = tmp;
path.add(0, node);
return path;
}
// find path to node in right sub-tree
tmp = pathToNode(root.right, node);
if (tmp != null && tmp.size() > 1) {
// path to node found; add result of recursion to current path
path = tmp;
path.add(0, node);
return path;
}
return null;
}
public Tree commonAncestor(Tree root, Tree p, Tree q) {
List<Tree> pathToP = pathToNode(root, p),
pathToQ = pathToNode(root, q);
// check whether both paths exist
if (pathToP == null || pathToQ == null) return null;
// walk both paths in parallel until the nodes differ
while (iterP.hasNext() && iterQ.hasNext() && iterP.next() == iterQ.next());
// return the previous matching node
return iterP.previous();
}
Both pathToNode and commonAncestor are in O(n).

Resources