Skip to main content

Find root of largest tree

Today we will discuss a popular interview question. The problem tests a developers hands-on on various aspect of Java programming language as well as his problem solving ability. The resultant solution must have optimum time and space complexity.

Problem Statement:

Find the root of largest tree in the forest. Given each tree is represented as a hashmap entry with child as the key and parent as the value. Higher the number of children for any given root larger the tree.

constrains:

a. One child has only one immediate parent

b. Any parent can have more than one children

c. If two trees are equal then return root with least id

Sample forest: {{1 -> 2}{3 -> 4}}

Here we have 2 trees, one with node 1 and root 2 and second with node 3 and root 4. Both trees have same size hence the output should be the node with least id ie. 2.

Solution: 

Approach: We receive input hashmap with key as child and value as parent, hence child nodes are unique in this collection but parent values can be duplicate for more than one keys. Here we don't get a view of how many children . Hence we need to maintain a different map where the parent will be key and number of children will be its value.

However even after doing this we will not know exactly how many trees are present in our forest. Number of trees in our forest equals number of root nodes. Root nodes are nodes which do not have a parent. Thus we can recursively check in the input hashmap if the parent of current element is also present in the map as key. This means our parent also has a parent. In this case we need to add that parent to our parent map and increment the count of its children as well.

For sample input as {{1->2}{3->4}{4->5}{6->2}}

The parent map will look like { 2-2, 4-2, 5-3 }

Now we just need to extract the entry with largest value from parent map.

The list of largest trees will look like {2,5}

 If there are more than one entries with highest number of children than we extract the least id and return that root. Stream API in Java 8 can achieve this easily.


import java.util.*;


public class Forest {

   
    HashMap<Integer,Integer> trees;   
    public int getLargestTree(HashMap<Integer,Integer> trees){
        this.trees = trees;
        //construct parent map
        Map<Integer,Integer> parentMap = createParentMap(
trees);
    
        //get largest trees
        List<Integer> largestTrees = getLargestTrees(parentMap);
    
        //get min id and return
        return largestTrees.stream().mapToInt(v->v).min().orElse(Integer.MAX_VALUE);

    }
    
    private Map<Integer,Integer> createParentMap(HashMap<Integer,Integer>
trees){
        Map<Integer,Integer> parentMap = new HashMap<>();
        for(Map.Entry<Integer,Integer> e :
trees.entrySet()){
            int parent = e.getValue();
            int value=parentMap.containsKey(parent)?parentMap.get(parent)+1:1;
            parentMap.put(parent,value);
            incrementChildCountForParent(parent,parentMap);
        }
        return parentMap;
    }
    
    private List<Integer> getLargestTrees(Map<Integer,Integer> parentMap){
        int maxChildren = 0;
        List<Integer> largestTrees = new ArrayList<>();
        for(Map.Entry<Integer,Integer> e : parentMap.entrySet()){
            if(e.getValue()>maxChildren){
                maxChildren = e.getValue();
                largestTrees.clear();
                largestTrees.add(e.getKey());
            }
            else if(e.getValue()==maxChildren){
                largestTrees.add(e.getKey());
            }
        }
        return largestTrees;
    }

    public void incrementChildCountForParent(int child, Map<Integer,Integer> parentMap) {
        
        if(trees.get(child)==null) return;
        
        int parent = trees.get(child);
        int value= parentMap.containsKey(parent)?parentMap.get(parent)+1:1;   
    
        parentMap.put(parent,value);
        
        incrementChildCountForParent(parent, parentMap);
    }
    
    public static void main(String args[]) {
      HashMap<Integer,Integer> forest = new HashMap<>();
      forest.put(1,2);
      forest.put(3,4);
      forest.put(4,5);
      forest.put(6,2);
      forest.put(7,4);
      System.out.println("Root of largest tree is "+new Forest().getLargestTree(forest));
      
    }
}

The time and space complexity of above program is O(n). This is because the time and space required varies as per the size of the input hashmap and the number of root nodes contained in it.

Is it possible to optimize this solution any further, currently I do not think so. However you can let me know your thoughts in the comments. Would love to see a insightful conversion.

Comments

Popular posts from this blog

Intercommunication in the world of micro services

I always wondered what can be the different ways in which the microservices can commmunicate with each other. Like majority of us I almost instintively thought of REST or SOAP as two prominant ways of passing data over internet, but are there other better ways of making the different components of an enterprise systen talk to each other? If yes what is the purpose of their existance, lets address these and other related queries in this article. The manner in which micro services talk to each other can be classified in four major categories: Synchronous Communication Asynchronous Communication Database Level Communication Hybrid Lets explore them one by one -  Synchronous Communication In this style of inter service communication a sender service requests some resource from its counterpart and then waits for the response before proceeding ahead. This is also called blocking mode because the caller is blocked till it gets wahat it had requested. Lets explore some protocols which use ...

Spark Checkpointing,Union, Lineage And Partioning

I have been working on apache spark lately and wanted to consolidated my learnings at one place so that I can refer later and make it available to other developers as well. While working with spark you will often run into situations where the way you write code normally results in most inefficient results. You can visualise spark plan executions but it is very tedious to navigate and make sense of in terms of what exactly is cause of the problem. In such case having basic knowledge of how spark manages tasks can help arrive at solutions to improve compute efficiency. Below are a set of techniques I have used to improve compute efficiency of spark applications. SparkContext with a Checkpoint Directory In  Apache Spark , the  SparkContext  is the entry point to interact with the Spark framework. It manages the execution of jobs, resource allocation, and coordination across clusters. A  checkpoint directory  in Spark is used to store  checkpointed RDDs or stre...

Java 21: 1 Hour Crash Course

Let’s embark on a  journey to discover the most important parts of Java 21 . I’ll cover the essentials, highlight new features in Java 21, and provide code samples and explanations. At the end of this article you’ll get a solid foundation and pointers for deeper study. Let's get started! Java 21: 1 Hour Crash Course 1. Introduction Java  is a powerful, object-oriented programming language. Java 21, released in September 2023, brings performance improvements and new features. Prerequisites: Basic programming concepts (variables, loops, functions). Java installed (JDK 21). 2. Your First Program public class HelloWorld { public static void main ( String [ ] args ) { System . out . println ( "Hello, Java 21!" ) ; } } public class HelloWorld  — Defines a class. public static void main  — Entry point. System.out.println  — Prints to console. 3. Variables and Data Types int age = 25 ; double price = 19.99 ; boolean isJavaFun = true ...