Get height of tree with multiple children - java

I'm having a hard time completing this algorithm to find the height of a tree with multiple children (not binary).
Anyone know where this is going wrong?
private int getHeight(Node<T> root, int height, int result){
if (root.getChildren().size()==0) {
return height;
}
height++;
//Iterate every child
for (Node<T> childNode : root.getChildren()) {
//Get the height again
height =getHeight(childNode, height, result);
//Update if new high result
if (height>result) {
result = height;
}
height = 0;
}
//Return highest point
return result;
}

You are making it harder by adding the height and result parameters.
In the function, find the height of each of the children. Keep the largest. Return 1 + height of the largest.
Sometrhing like this (untested, uncompiled):
private int getHeight(Node<T> root){
int max = 0;
for (Node<T> childNode : root.getChildren()) {
int height = getHeight(childNode);
if (height > max)
max = height;
}
return max + 1;
}

The way you are calculating the height is very awkward and there are many places for possible errors, such as you adding one to the height then getting the height of the children.
I would suggest doing a simpler recursive function that is similar to the one that you are doing.
First of all, you can get rid of the second and third parameters, then you can change the code to look something more like this:
private int getHeight(Node<T> root){
if (root.getChildren().size()==0) {
return 1;
}
int height;
//Iterate every child
for (Node<T> childNode : root.getChildren()) {
//Get the height again
int childHeight = getHeight(childNode);
//Update if new high result
if (childHeight>height) {
height = childHeight;
}
}
//Return highest point
return height + 1;
}
This just returns 1 if the node has no children. Otherwise, it gets the max height of all children and returns that number plus 1.

Related

Binary tree breadth first search algorithm

In a binary tree BFS algorithm, can someone please help me understand why we do a height - 1 in the code below. I wrote this code but it never worked until I figured out online you need to do a height - 1.
public class BreadthFirstSearch {
public static int calculateHeightOfTree(Node root) {
if (root == null) {
return 0;
} else {
return 1 + Math.max(calculateHeightOfTree(root.leftNode), calculateHeightOfTree(root.rightNode));
}
}
public static void printDataAtAllLevels(Node root, int height) {
for (int i = 1; i <= height; i++) {
printDataAtGivenLevel(root, i);
}
}
public static void printDataAtGivenLevel(Node root, int height) {
if (root == null) {
return;
}
if (height == 1) {
System.out.println(root.data);
} else {
printDataAtGivenLevel(root.leftNode, height - 1);
printDataAtGivenLevel(root.rightNode, height - 1);
}
}
public static void main(String[] args) {
Node node = new Node(1);
node.leftNode = new Node(2);
node.rightNode = new Node(3);
node.leftNode.leftNode = new Node(4);
node.leftNode.rightNode = new Node(5);
System.out.println("Level order traversal of binary tree is ");
int height = calculateHeightOfTree(node);
System.out.println("HEIGHT: " + height);
printDataAtAllLevels(node, height);
}
Well, if you want to print the data of level n of the tree, that's equivalent to printing the data of level n-1 of the left and right sub-trees. Therefore, when you pass the left and right sub-trees to the recursive calls, you should request to print the data of level reduced by 1.
For example, since the root of the tree has level 1, the left and right children of the root have level 2.
So if you wish to print all the data of level 2 for the original tree, that's equivalent to printing the data of level 1 for the left and right sub-trees.
If you would not decrease height it would always be the same value in every (recursive) method call.
Therefore the recursion would not stop because height == 1 would always be false. It would only stop because root == null would be true, because you reached the end of a sub-tree. But in this case there would be no output, but only a return.
Because the height int printDataAtGivenLevel(Node root, int height) is the height relative to the root. So if you want to print level 2 from the root, you need to print level 1 from root.left and root.right.
So that you can print the height starting from the node with the lowest height to the node with the maximum height.
Honestly, when I read Binary tree breadth first search algorithm, I do not think about a series of depth-limited DFS traversals, but visiting nodes of a given level, and collecting the ones for the next level, rinse and repeat:
static void doStuff(Node root){
List<Node> current=new ArrayList<>();
current.add(root);
int level=0;
int total=0;
while(current.size()>0){
level++;
System.out.println("Level "+level+":");
List<Node> next=new ArrayList<>();
for(int i=0;i<current.size();i++){
Node node=current.get(i);
System.out.print(node.data+" ");
if(node.leftNode!=null)
next.add(node.leftNode);
if(node.rightNode!=null)
next.add(node.rightNode);
total++;
}
System.out.println();
current=next;
}
System.out.println(total+" nodes visited, from "+level+" levels");
}
Then it can be tricked into one list:
static void doStuff(Node root){
List<Node> nodes=new LinkedList<>();
nodes.add(root);
int level=0;
int total=0;
int current;
while((current=nodes.size())>0){
level++;
System.out.println("Level "+level+":");
while(current-->0){
Node node=nodes.removeFirst();
System.out.print(node.data+" ");
if(node.leftNode!=null)
nodes.add(node.leftNode);
if(node.rightNode!=null)
nodes.add(node.rightNode);
total++;
}
System.out.println();
}
System.out.println(total+" nodes visited, from "+level+" levels");
}

Merge communities and find a size of the largest one

I'm struggling with this problem on HackerRank.
https://www.hackerrank.com/challenges/friend-circle-queries/problem
I tried solving it using a custom linked list - NodeList. It has three fields - Node first, Node current, int size. 'add' is an overloaded method. It can add a value or another NodeList.
I have put code for NodeList in comments because it doesn't matter much.
Fields :-
static HashMap<Integer, Integer> personToIndex = new HashMap<>();
static int largestCircleSize = 0;
static ArrayList<NodeList> groups = new ArrayList<>();
This is my business logic method.
When only one person is part of a friend circle, I add the other person in the circle. When both the people who are shaking hands are already part of other circles, I merge the circles.
static void updateFriendCircles(int friend1, int friend2) {
int friend1Index, friend2Index;
NodeList toUpdate;
friend1Index = personToIndex.getOrDefault(friend1, -1);
friend2Index = personToIndex.getOrDefault(friend2, -1);
if (friend1Index != -1) {
NodeList list = groups.get(friend1Index);
if (friend2Index != -1) {
NodeList list2 = groups.get(friend2Index);
if (list.first == groups.get(friend2Index).first)
return;
toUpdate = list.add(list2);
groups.set(friend2Index, list);
}
else {
toUpdate = list.add(friend2);
personToIndex.put(friend2, friend1Index);
}
}
else if (friend2Index != -1) {
toUpdate = groups.get(friend2Index).add(friend1);
personToIndex.put(friend1, friend2Index);
}
else {
int index = groups.size();
personToIndex.put(friend1, index);
personToIndex.put(friend2, index);
toUpdate = new NodeList(friend1).add(friend2);
groups.add(toUpdate);
}
if (toUpdate.size > largestCircleSize)
largestCircleSize = toUpdate.size;
}
I have also tried using HashSet but it also has same problem so I think problem is not in data structure.
As it's not clear what is wrong with the solution exactly (it's not specified by the OP) - wrong answers or timeout for some test cases I'll explain how to solve it.
We can use a disjoint set data structure to represent sets of friend circles.
The basic idea is that in each circle we assign a member that is used to represent a given circle. We can call it a root. Finding a number of members in the circle is always delegated to the root that stores its size.
Each non-root member points to his root member or to a member through whom he can get to the root. In the future, the current root may also lose his root status for the community but then it will point to the new root and so it's always possible to get to it through chained calls.
When 2 circles merge then a new root member is chosen among 2 previous ones. The new size can be set into it because previous roots already contain sizes for both circles. But how is the new root chosen? If the size of circle 1 is not smaller than that of circle 2 then it's picked as a new root.
So, for this problem, first we should define placeholders for circles and sizes:
Map<Integer, Integer> people;
Map<Integer, Integer> sizes;
For non-root members in people, key is a person ID and value is a friend he follows (root or parent that can get him to the root). Root members won't have an entry in the map.
Then, we need a method that will get us to the root for any member:
int findCommunityRoot(int x) {
if (people.containsKey(x)) {
return findCommunityRoot(people.get(x));
}
return x;
}
Finally, we need a method to build a community for 2 given friends:
int mergeCommunities(int x, int y) {
//find a root of x
x = findCommunityRoot(x);
//find a root of y
y = findCommunityRoot(y);
// one-man circle has a size of 1
if (!sizes.containsKey(x)) {
sizes.put(x, 1);
}
// one-man circle has a size of 1
if (!sizes.containsKey(y)) {
sizes.put(y, 1);
}
// friends in the same circle so just return its size
if (x == y) {
return sizes.get(x);
}
sizeX = sizes.get(x);
sizeY = sizes.get(y);
if (sizeX >= sizeY) {
people.put(y, x);
sizes.put(x, sizeY + sizeX);
return sizes.get(x);
} else {
people.put(x, y);
sizes.put(y, sizeY + sizeX);
return sizes.get(y);
}
}
So, we have everything we need to save a size of the largest circle at each iteration:
List<Integer> maxCircle(int[][] queries) {
List<Integer> maxCircles = new ArrayList<>();
int maxSize = 1;
for (int i = 0; i < queries.length; i++) {
int size = mergeCommunities(queries[i][0], queries[i][1]);
maxSize = Math.max(maxSize, size);
maxCircles.add(maxSize);
}
return maxCircles;
}

Maximum Height of a Binary Tree

Hi I came across a code to find the maximum height of a binary tree.
In this code why is there a +1 in the return statement?
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
If there wasn't, the result would always be 0.
The max height of a binary tree is the max height of the child having a larger max height (that's the Math.max(maxDepth(root.left), maxDepth(root.right)) part) + 1 for the root of the tree.
Consider a tree consisting of a single root node. Then the following return statement would return 1, which is what we expect:
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
return Math.max(0, 0) + 1;
return 1;
As your base case shows, an empty tree would have a height of zero, and you can build up to taller trees' height using induction.

calculate height of none-binary tree

How can I generalize the recursive code for finding the height of binary tree to non-binary trees? I checked Non-binary tree height. But there was just a pseudocode. So far I have wrote the following which is shooting me the wrong answer:
public static <MyType> int calculateHeight(MyTreeNode<MyType> r){
if (r ==null)
return -1;
if (r.children.size()==0)
return 0;
int count=0;
List<Integer> heights = new ArrayList<>();
for (MyTreeNode<MyType> e : r.children)
count = calculateHeight(e)+1;
heights.add(count);
return max(heights);
}
You have a problem of missing braces. You should add count to heights list for each of the children. You only added the count for the last child, which means you calculated the height of the right-most path in the tree (assuming the last child in your children list is the right-most child).
public static <MyType> int calculateHeight(MyTreeNode<MyType> r){
if (r ==null)
return 0;
if (r.children.size()==0)
return 1;
int count=0;
List<Integer> heights = new ArrayList<>();
for (MyTreeNode<MyType> e : r.children) {
count = calculateHeight(e)+1;
heights.add(count);
}
return max(heights);
}

Re-Sizing Hash Table

I'm attempting to resize my hash table however; I am keep getting a NullPointerException.
I know if the size is greater than 0.75 then the table size has to double, if it's less than 0.50 then the table size is halved. So far I have this..
public boolean add(Object x)
{
int h = x.hashCode();
if (h < 0) { h = -h; }
h = h % buckets.length;
Node current = buckets[h];
while (current != null)
{
if (current.data.equals(x)) { return false; }
// Already in the set
current = current.next;
}
Node newNode = new Node();
newNode.data = x;
newNode.next = buckets[h];
buckets[h] = newNode;
currentSize++;
double factor1 = currentSize * load1; //load1 = 0.75
double factor2 = currentSize * load2; //load2 = 0.50
if (currentSize > factor1) { resize(buckets.length*2); }
if (currentSize < factor2) { resize(buckets.length/2); }
return true;
}
Example. Size = 3. Max Size = 5
if we take the Max Size and multiply by 0.75 we get 3.75.
this is the factor that says if we pass it the Max Size must double
so if we add an extra element into the table the size is 4 and is > 3.75 thus the new Max Size is 10.
However; once we increase the size, the hashcode will change with the addition of a new element, so we call resize(int newSize)
private void resize(int newLength)
{
//
HashSet newTable = new HashSet(newLength);
for (int i = 0; i < buckets.length; i++) {
newTable.add(buckets[i]);
}
}
Here is my constructor if the buckets[i] confuses anyone.
public HashSet(int bucketsLength)
{
buckets = new Node[bucketsLength];
currentSize = 0;
}
I feel that the logic is correct, unless my resize method is not retrieving the elements.
If that is all your code for resize(), then you are failing to assign newTable to a class attribute, i.e. your old table. Right now you fill it with data and then don't do anything with it, since it is defined inside resize and therefore not available outside of it.
So you end up thinking you have a larger table now, but in fact you are still using the old one ;-)

Categories

Resources