Given a binary tree with n nodes and a non-negative integer k, the task is to count the number of special nodes.
A node is considered special if there exists at least one leaf in its subtree such that the distance between the node and leaf is exactly k.
Any such node should be counted only once. For example, if a node is at a distance k from 2 or more leaf nodes, then it would add only 1 to the count.
Examples:
Input: root[] = [1, 2, 3, 4, 5, 6, 7, N, N, N, N, N, 8], k = 2
Output: 2 Explanation: There are only two unique nodes that are at a distance of 2 units from the leaf node. (node 3 for leaf with value 8 and node 1 for leaves with values 4, 5 and 7) Note that node 2 isn't considered for leaf with value 8 because it isn't a direct ancestor of node 8.
[Naive Approach] Check Every Node Separately - O(n ^ 2) Time and O(h) Space
The idea is to consider every node as a starting node and check whether there exists a leaf in its subtree at exactly distance k. If such a leaf exists, increment the answer.
Working of Approach:
Traverse every node of the binary tree using DFS, treating each node as a potential starting point.
For each node, recursively search its subtree to check whether there exists a leaf exactly k edges below it.
C++
#include<iostream>usingnamespacestd;classNode{public:intdata;Node*left,*right;Node(intval){data=val;left=right=nullptr;}};// Returns true if there is a leaf exactly k edges below node.boolcheck(Node*root,intdist,intk){if(!root)returnfalse;// Leaf nodeif(!root->left&&!root->right)returndist==k;returncheck(root->left,dist+1,k)||check(root->right,dist+1,k);}// Visit every node and check it separately.voiddfs(Node*root,intk,int&ans){if(!root)return;if(check(root,0,k))ans++;dfs(root->left,k,ans);dfs(root->right,k,ans);}intkthFromLeaf(Node*root,intk){intans=0;dfs(root,k,ans);returnans;}intmain(){/* 1 / \ 2 3 / \ / \ 4 5 6 7 \ 8 */Node*root=newNode(1);root->left=newNode(2);root->right=newNode(3);root->left->left=newNode(4);root->left->right=newNode(5);root->right->left=newNode(6);root->right->right=newNode(7);root->right->left->right=newNode(8);intk=2;cout<<kthFromLeaf(root,k);return0;}
Java
classNode{intdata;Nodeleft,right;Node(intval){data=val;left=right=null;}}classGFG{// Returns true if there is a leaf exactly k edges below// node.staticbooleancheck(Noderoot,intdist,intk){if(root==null)returnfalse;// Leaf nodeif(root.left==null&&root.right==null)returndist==k;returncheck(root.left,dist+1,k)||check(root.right,dist+1,k);}// Visit every node and check it separately.staticvoiddfs(Noderoot,intk,int[]ans){if(root==null)return;if(check(root,0,k))ans[0]++;dfs(root.left,k,ans);dfs(root.right,k,ans);}staticintkthFromLeaf(Noderoot,intk){int[]ans={0};dfs(root,k,ans);returnans[0];}publicstaticvoidmain(String[]args){/* 1 / \ 2 3 / \ / \ 4 5 6 7 \ 8 */Noderoot=newNode(1);root.left=newNode(2);root.right=newNode(3);root.left.left=newNode(4);root.left.right=newNode(5);root.right.left=newNode(6);root.right.right=newNode(7);root.right.left.right=newNode(8);intk=2;System.out.println(kthFromLeaf(root,k));}}
Python
classNode:def__init__(self,val):self.data=valself.left=Noneself.right=None# Returns true if there is a leaf exactly k edges below node.defcheck(root,dist,k):ifrootisNone:returnFalse# Leaf nodeifroot.leftisNoneandroot.rightisNone:returndist==kreturncheck(root.left,dist+1,k)orcheck(root.right,dist+1,k)# Visit every node and check it separately.defdfs(root,k,ans):ifrootisNone:returnifcheck(root,0,k):ans[0]+=1dfs(root.left,k,ans)dfs(root.right,k,ans)defkthFromLeaf(root,k):ans=[0]dfs(root,k,ans)returnans[0]if__name__=="__main__":""" 1 / \ 2 3 / \ / \ 4 5 6 7 \ 8 """root=Node(1)root.left=Node(2)root.right=Node(3)root.left.left=Node(4)root.left.right=Node(5)root.right.left=Node(6)root.right.right=Node(7)root.right.left.right=Node(8)k=2print(kthFromLeaf(root,k))
C#
usingSystem;classNode{publicintdata;publicNodeleft,right;publicNode(intval){data=val;left=right=null;}}classGFG{// Returns true if there is a leaf exactly k edges below// node.staticboolCheck(Noderoot,intdist,intk){if(root==null)returnfalse;// Leaf nodeif(root.left==null&&root.right==null)returndist==k;returnCheck(root.left,dist+1,k)||Check(root.right,dist+1,k);}// Visit every node and check it separately.staticvoidDfs(Noderoot,intk,refintans){if(root==null)return;if(Check(root,0,k))ans++;Dfs(root.left,k,refans);Dfs(root.right,k,refans);}staticintkthFromLeaf(Noderoot,intk){intans=0;Dfs(root,k,refans);returnans;}staticvoidMain(){/* 1 / \ 2 3 / \ / \ 4 5 6 7 \ 8 */Noderoot=newNode(1);root.left=newNode(2);root.right=newNode(3);root.left.left=newNode(4);root.left.right=newNode(5);root.right.left=newNode(6);root.right.right=newNode(7);root.right.left.right=newNode(8);intk=2;Console.WriteLine(kthFromLeaf(root,k));}}
JavaScript
// Returns true if there is a leaf exactly k edges below// node.functioncheck(root,dist,k){if(root===null)returnfalse;// Leaf nodeif(root.left===null&&root.right===null)returndist===k;returncheck(root.left,dist+1,k)||check(root.right,dist+1,k);}// Visit every node and check it separately.functiondfs(root,k,ans){if(root===null)return;if(check(root,0,k))ans.count++;dfs(root.left,k,ans);dfs(root.right,k,ans);}functionkthFromLeaf(root,k){letans={count:0};dfs(root,k,ans);returnans.count;}// Helper function to create a nodefunctionnewNode(val){return{data:val,left:null,right:null};}/* 1 / \ 2 3 / \ / \ 4 5 6 7 \ 8*/letroot=newNode(1);root.left=newNode(2);root.right=newNode(3);root.left.left=newNode(4);root.left.right=newNode(5);root.right.left=newNode(6);root.right.right=newNode(7);root.right.left.right=newNode(8);letk=2;console.log(kthFromLeaf(root,k));
Output
2
[Expected Approach] Single DFS with Path Level Marking - O(n) Time and O(h) Space
The idea is to perform a DFS while maintaining the current root-to-node path. Whenever a leaf is reached, its k-th ancestor is found using the current path. A visited[] array indexed by the current path level ensures that the same ancestor is counted only once, even if multiple leaf nodes share the same k-th ancestor.
Working of Approach:
Traverse the binary tree using DFS, while maintaining the current path length from the root to the current node.
Whenever a leaf node is reached, its k-th ancestor is located using the index pathLen - k - 1.
A visited[] array is used to mark ancestors that have already been counted, preventing duplicate counting when multiple leaves share the same k-th ancestor.
Continue the DFS for both left and right subtrees until all root-to-leaf paths have been explored.
The final counter gives the number of unique nodes that are exactly k distance away from at least one leaf node.
Let us understand with an example: Input: root[] = [1, 2, 3, 4, 5, 6, 7, N, N, N, N, N, 8], k = 2
Start the DFS from the root (1) with pathLen = 0, recursively exploring all root-to-leaf paths.
On reaching leaf nodes 4 and 5, their 2nd ancestor is node 1; it is counted only once by marking its level as visited.
Next, the traversal reaches leaf node 8, whose 2nd ancestor is node 3; since it has not been counted before, increment the counter.
Finally, for leaf node 7, the 2nd ancestor is again node 1, which is already marked, so it is not counted again.
After all paths are processed, the counter becomes 2, representing the unique nodes (1 and 3) that are exactly 2 distance away from at least one leaf node.
C++
#include<iostream>usingnamespacestd;#define MAX_HEIGHT 100005classNode{public:intdata;Node*left,*right;Node(intval){data=val;left=right=nullptr;}};voidcountNodes(Node*node,Node*path[],int&counter,boolvisited[],intpathLen,intk){// base caseif(node==nullptr)return;path[pathLen]=node;visited[pathLen]=false;pathLen++;// if it's a leaf node, we increment the count but only if the// same ancestor at distance k is not already counted.if(node->left==nullptr&&node->right==nullptr&&pathLen-k-1>=0&&visited[pathLen-k-1]==false){counter++;// setting the ancestor as visited so that we won't count it again.visited[pathLen-k-1]=true;return;}// if the current node is not a leaf node then we call the function// recursively for left and right subtrees.countNodes(node->left,path,counter,visited,pathLen,k);countNodes(node->right,path,counter,visited,pathLen,k);}// Function to return count of nodes at a given distance from leaf nodes.intkthFromLeaf(Node*root,intk){intcounter=0;boolvisited[MAX_HEIGHT]={false};Node*path[MAX_HEIGHT];countNodes(root,path,counter,visited,0,k);// returning the count.returncounter;}intmain(){/* 1 / \ 2 3 / \ / \ 4 5 6 7 \ 8 */Node*root=newNode(1);root->left=newNode(2);root->right=newNode(3);root->left->left=newNode(4);root->left->right=newNode(5);root->right->left=newNode(6);root->right->right=newNode(7);root->right->left->right=newNode(8);intk=2;cout<<kthFromLeaf(root,k);return0;}
Java
classNode{intdata;Nodeleft,right;Node(intval){data=val;left=right=null;}}classGFG{staticfinalintMAX_HEIGHT=100005;staticvoidcountNodes(Nodenode,Node[]path,int[]counter,boolean[]visited,intpathLen,intk){// base caseif(node==null)return;path[pathLen]=node;visited[pathLen]=false;pathLen++;// if it's a leaf node, we increment the count but// only if the same ancestor at distance k is not// already counted.if(node.left==null&&node.right==null&&pathLen-k-1>=0&&visited[pathLen-k-1]==false){counter[0]++;// setting the ancestor as visited so that we// won't count it again.visited[pathLen-k-1]=true;return;}// if the current node is not a leaf node then we// call the function recursively for left and right// subtrees.countNodes(node.left,path,counter,visited,pathLen,k);countNodes(node.right,path,counter,visited,pathLen,k);}// Function to return count of nodes at a given distance// from leaf nodes.staticintkthFromLeaf(Noderoot,intk){int[]counter={0};boolean[]visited=newboolean[MAX_HEIGHT];Node[]path=newNode[MAX_HEIGHT];countNodes(root,path,counter,visited,0,k);// returning the count.returncounter[0];}publicstaticvoidmain(String[]args){/* 1 / \ 2 3 / \ / \ 4 5 6 7 \ 8 */Noderoot=newNode(1);root.left=newNode(2);root.right=newNode(3);root.left.left=newNode(4);root.left.right=newNode(5);root.right.left=newNode(6);root.right.right=newNode(7);root.right.left.right=newNode(8);intk=2;System.out.println(kthFromLeaf(root,k));}}
Python
MAX_HEIGHT=100005classNode:def__init__(self,val):self.data=valself.left=Noneself.right=NonedefcountNodes(node,path,counter,visited,pathLen,k):# base caseifnodeisNone:returnpath[pathLen]=nodevisited[pathLen]=FalsepathLen+=1# if it's a leaf node, we increment the count but only if the# same ancestor at distance k is not already counted.if(node.leftisNoneandnode.rightisNoneandpathLen-k-1>=0andvisited[pathLen-k-1]==False):counter[0]+=1# setting the ancestor as visited so that we won't count it again.visited[pathLen-k-1]=Truereturn# if the current node is not a leaf node then we call the function# recursively for left and right subtrees.countNodes(node.left,path,counter,visited,pathLen,k)countNodes(node.right,path,counter,visited,pathLen,k)# Function to return count of nodes at a given distance from leaf nodes.defkthFromLeaf(root,k):counter=[0]visited=[False]*MAX_HEIGHTpath=[None]*MAX_HEIGHTcountNodes(root,path,counter,visited,0,k)# returning the count.returncounter[0]if__name__=="__main__":""" 1 / \ 2 3 / \ / \ 4 5 6 7 \ 8 """root=Node(1)root.left=Node(2)root.right=Node(3)root.left.left=Node(4)root.left.right=Node(5)root.right.left=Node(6)root.right.right=Node(7)root.right.left.right=Node(8)k=2print(kthFromLeaf(root,k))
C#
usingSystem;classNode{publicintdata;publicNodeleft,right;publicNode(intval){data=val;left=right=null;}}classGFG{constintMAX_HEIGHT=100005;staticvoidCountNodes(Nodenode,Node[]path,refintcounter,bool[]visited,intpathLen,intk){// base caseif(node==null)return;path[pathLen]=node;visited[pathLen]=false;pathLen++;// if it's a leaf node, we increment the count but// only if the same ancestor at distance k is not// already counted.if(node.left==null&&node.right==null&&pathLen-k-1>=0&&visited[pathLen-k-1]==false){counter++;// setting the ancestor as visited so that we// won't count it again.visited[pathLen-k-1]=true;return;}// if the current node is not a leaf node then we// call the function recursively for left and right// subtrees.CountNodes(node.left,path,refcounter,visited,pathLen,k);CountNodes(node.right,path,refcounter,visited,pathLen,k);}// Function to return count of nodes at a given distance// from leaf nodes.staticintkthFromLeaf(Noderoot,intk){intcounter=0;bool[]visited=newbool[MAX_HEIGHT];Node[]path=newNode[MAX_HEIGHT];CountNodes(root,path,refcounter,visited,0,k);// returning the count.returncounter;}staticvoidMain(){/* 1 / \ 2 3 / \ / \ 4 5 6 7 \ 8 */Noderoot=newNode(1);root.left=newNode(2);root.right=newNode(3);root.left.left=newNode(4);root.left.right=newNode(5);root.right.left=newNode(6);root.right.right=newNode(7);root.right.left.right=newNode(8);intk=2;Console.WriteLine(kthFromLeaf(root,k));}}
JavaScript
constMAX_HEIGHT=100005;functionnewNode(val){return{data:val,left:null,right:null};}functioncountNodes(node,path,counter,visited,pathLen,k){// base caseif(node===null)return;path[pathLen]=node;visited[pathLen]=false;pathLen++;// if it's a leaf node, we increment the count but only// if the same ancestor at distance k is not already// counted.if(node.left===null&&node.right===null&&pathLen-k-1>=0&&visited[pathLen-k-1]===false){counter.count++;// setting the ancestor as visited so that we won't// count it again.visited[pathLen-k-1]=true;return;}// if the current node is not a leaf node then we call// the function recursively for left and right subtrees.countNodes(node.left,path,counter,visited,pathLen,k);countNodes(node.right,path,counter,visited,pathLen,k);}// Function to return count of nodes at a given distance// from leaf nodes.functionkthFromLeaf(root,k){letcounter={count:0};letvisited=newArray(MAX_HEIGHT).fill(false);letpath=newArray(MAX_HEIGHT);countNodes(root,path,counter,visited,0,k);// returning the count.returncounter.count;}/* 1 / \ 2 3 / \ / \ 4 5 6 7 \ 8*/letroot=newNode(1);root.left=newNode(2);root.right=newNode(3);root.left.left=newNode(4);root.left.right=newNode(5);root.right.left=newNode(6);root.right.right=newNode(7);root.right.left.right=newNode(8);letk=2;console.log(kthFromLeaf(root,k));