I am trying to find the diameter of a binary tree (Path length between any two nodes in the tree containing maximum number of nodes.) in java.
my code snippet:
public int diametre(Node node, int d)
{
if(node==null)
return 0;
lh=diametre(node.left, d);
rh=diametre(node.right, d);
if(lh+rh+1>d)
d=lh+rh+1;
return findMax(lh, rh)+1;
}
In main method:
System.out.println( bst.diametre(root,0) );
Logic:
Its actually post-order logic. variable 'd' refers to the diameter of the sub-tree (In that iteration.). It will be updated as and when some larger value found.
'lh' refers to : Left sub tree's height.
'rh' refers to : right sub tree's height.
But its giving wrong output.
Tree considered:
5
/ \
/ \
1 8
\ /\
\ / \
3 6 9
Idle output: 5
But this code is giving 3.
Can some one figure out where the problem is...
public int diameter (Node root)
{
if (root == null) return 0;
else return Math.max (
diameter (root.left),
Math.max (
diameter (root.right),
height (root.left) + height (root.right) + 1));
}
public int height (Node root)
{
if (root == null) return 0;
else return 1 + Math.max (height (root.left), height (root.right));
}
You find a diameter of a tree by running a BFS from any node and then another BFS from the furthest node(the node last visited during the first BFS). The diameter is formed by the node last visited in the first BFS and the node last visited in the first BFS. The fact that the tree is binary does not affect the algorithm.
EDIT: changing the value of d in the code you have written will not affect the argument you pass as primitive types are not passed by reference in java.
I suggest the following:
public static TreeAttr calcTreeDiameter(Node root) {
if (root == null)
return new TreeAttr(0, 0);
TreeAttr leftAttr = calcTreeDiameter(root.getLeft());
TreeAttr rightAttr = calcTreeDiameter(root.getRight());
int maxDepth = Math.max(leftAttr.depth, rightAttr.depth);
int maxDiam = Math.max(leftAttr.diameter, rightAttr.diameter);
maxDiam = Math.max(maxDiam, leftAttr.depth + rightAttr.depth + 1);
return new TreeAttr(maxDiam, maxDepth + 1);
}
The TreeAttr is a simple structure containing the diameter and depth of a subtree. Both should be passed in the recursion, since the optimum may either come from one of the subtrees, or from the concatenation of the longest paths.
int max=0;
public int diameter(Tree root) {
if(root==null) return 0;
int l=diameter(root.left);
int r=diameter(root.right);
max=Math.max(max,l+r+1);
return l>r:l+1:r+1;
}
max is the max diameter.
Algo takes O(n). Calculates height and path at the same time.
public static int findLongestPath(TreeNode root)
{
// longest path = max (h1 + h2 + 2, longestpath(left), longestpath(right);
int[] treeInfo = longestPathHelper(root);
return treeInfo[0];
}
private static int[] longestPathHelper(TreeNode root)
{
int[] retVal = new int[2];
if (root == null)
{
//height and longest path are 0
retVal[0] = 0;
retVal[1] = 0;
}
int[] leftInfo = longestPathHelper(root.getLeft());
int[] rightInfo = longestPathHelper(root.getRight());
retVal[0] = Math.max(leftInfo[1] + rightInfo[1] + 2, Math.max(leftInfo[0], rightInfo[0]));
retVal[1] = Math.max(leftInfo[1], rightInfo[1]) + 1;
return retVal;
}
You should use the height of the tree to calculate the diameter. Make a getHeight() function which will take the root of the tree as argument and return the height of the tree. Using this value and with the help of recursion we can calculate the diameter of the tree. Here is the code for it.....
Function for calculating the diameter :-
public static int getDiameter(BinaryTreeNode root) {
if (root == null)
return 0;
int rootDiameter = findHeight(root.getLeft()) + findHeight(root.getRight()) + 1;
int leftDiameter = getDiameter(root.getLeft());
int rightDiameter = getDiameter(root.getRight());
return Math.max(rootDiameter, Math.max(leftDiameter, rightDiameter));
}
Function for calculating the height of tree:-
public static int findHeight(BinaryTreeNode node) {
if(node == null)
return 0;
else {
return 1+Math.max(findHeight(node.left), findHeight(node.right));
}
}
Diameter of a Binary Tree in O(n), It will track the diameter passing through root node or not and uses the same height function to track the diameter.
DiameterOfTree.class
import BinaryTree.BinaryTreeNode;
public class DiameterOfBinaryTree {
private int DIAMETER = 0;
public void getDiameterOfBinaryTree(BinaryTreeNode node) {
getHeightUtil(node, DIAMETER);
System.out.print("\n\n Maximum Diameter of the tree is : " + DIAMETER);
}
private int getHeightUtil(BinaryTreeNode node, Integer maXValue) {
if (node == null) {
return 0;
}
// Here we get the maximum value returned + 1 for each subtree left or
// right
int leftHeight = getHeightUtil(node.getLeft(), maXValue);
int rightHeight = getHeightUtil(node.getRight(), maXValue);
//finding the new diameter at a particular node and adding 1 to
//include that particular node as well: leftHeight + rightHeight + 1
DIAMETER = Math.max(DIAMETER, leftHeight + rightHeight + 1);
return 1 + Math.max(leftHeight, rightHeight);
}
}
Main.java
package BinaryTree;
public class Main {
public static void main(String[] args) {
//Initialise root
BinaryTreeNode root = new BinaryTreeNode(40);
//Create a binary Tree
InitialiseBinaryTree initialiseBinaryTree = new InitialiseBinaryTree();
initialiseBinaryTree.initialise(root);
// Find the diameter of the binary tree
new DiameterOfBinaryTree().getDiameterOfBinaryTree(root);
}
}
InitialiseBinaryTree.java
package BinaryTree;
class InitialiseBinaryTree {
void initialise(BinaryTreeNode root) {
BinaryTreeOperation bto = new BinaryTreeOperation();
int[] data = {20, 50, 10, 30, 5,8, 25, 32, 33};
for (int aData : data) {
bto.insertElementInBinaryTree(root, aData);
}
}
}
BinaryTreeOperation.java
package BinaryTree;
class BinaryTreeOperation {
private boolean findInBinaryTree(BinaryTreeNode node, int data) {
return node != null &&
(data == node.getData() || (findInBinaryTree(node.getLeft(), data) || findInBinaryTree(node.getRight(), data)));
}
void insertElementInBinaryTree(BinaryTreeNode node, int data) {
if (node == null) {
new BinaryTreeNode(data);
} else {
insertHelper(node, data);
}
}
private void insertHelper(BinaryTreeNode node, int data) {
if (node.getData() > data) {
if (node.getLeft() == null) {
node.setLeft(new BinaryTreeNode(data));
} else {
insertHelper(node.getLeft(), data);
}
} else {
if (node.getRight() == null) {
node.setRight(new BinaryTreeNode(data));
} else {
insertHelper(node.getRight(), data);
}
}
}
}
Related
I am working on Leetcode question 437 Path Sum III, and solving it use DFS on java:
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public static int pathSum(TreeNode root, int sum) {
return dfs(root, sum)+pathSum(root.left, sum)+pathSum(root.right, sum);
}
public static int dfs(TreeNode root, int sum) {
if (root == null) return 0;
int count = 0;
if (root.val == sum) count++;
count += dfs(root.left, sum - root.val);
count += dfs(root.right, sum - root.val);
return count;
}
In the return statement of pathSum() method, why we need "dfs(root, sum)+dfs(root.left, sum)+dfs(root.right, sum)", not simply "dfs(root, sum)(this one returns wrong answer)"?
Someone explains that is because "The path does not need to start or end at the root or a leaf "(from lc437). If so, then why we only need to also only check root's children, not also the children of root's children?
To avoid NullPointerException you need to make a small change in pathSum:
public static int pathSum(TreeNode root, int sum) {
if( root == null) return 0;
return dfs(root, sum)+pathSum(root.left, sum)+pathSum(root.right, sum);
}
Consider the given tree:
Now let's transverse the tree from the root node searching for a path with a length of 8.
This can do it by omitting +pathSum(root.left, sum)+pathSum(root.right, sum); from pathSum:
public static int pathSum(TreeNode root, int sum) {
if( root == null) return 0;
//check root only
return dfs(root, sum);//+pathSum(root.left, sum)+pathSum(root.right, sum);
}
This return 0 because there is no path, starting at the root, with the length of 0.
So now we want to check the sub trees. Is there any path with a length of 8 starting at root.right ? We can do it like so:
public static int pathSum(TreeNode root, int sum) {
if( root == null) return 0;
//check root, check root.right and return the sum
return dfs(root, sum) + pathSum(root.right, sum) ;//+pathSum(root.left, sum);
}
This should return 1 because there is one path starting atroot.right with the length of 8: -3 -> 11
I hope this clarifies why we need to check root as well as left and right for the complete result.
Side note: you can get the same result by checking all tree node in a non-recursive manner. For example:
Stack<TreeNode> stack = new Stack<>();
stack.add(root);
int count = 0;
while (! stack.isEmpty()){
TreeNode node = stack.pop();
count += dfs(node,8);
if(node != null) {
stack.add(node.left);
stack.add(node.right);
}
}
System.out.println(count);
Because by going left and right on the tree we will traverse it. We have to traverse the tree in order to find the path sum. If you only go to root then you will not move in the tree thus resulting in the wrong answer.
public static int pathSum(TreeNode root, int sum) {
return dfs(root, sum)+pathSum(root.left, sum)+pathSum(root.right, sum);
}
What this does is it treats each of the nodes as the root of a subtree on which it calculates the path and by calculating the sum of the path of the smaller trees you will obtain the sum of the paths of the larger tree.
I hope that this helps.
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");
}
I am trying to create a min heap but I am running into the issue where the numbers that are being displayed in my min heap are all in random order and there are extra 0's where there should be different values. This is the code for my class that does most of the work:
public class Heap211 {
static Random rand = new Random();
static public int[] Heap;
static public int size;
Heap211(){
Heap = new int[30];
size = 0;
}
static public int parent(int index){//location of parent
return index / 2;//array[k / 2]
}
static public int leftChild(int index){//location of left child
return index * 2;//array[k * 2]
}
static public int rightChild(int index){//location of right child
return index * 2 + 1;//array[k * 2 + 1]
}
static public boolean hasParent(int index){
return index > 1;
}
static public boolean hasLeftChild(int index){
return leftChild(index) * 2 <= size;
}
static public boolean hasRightChild(int index){
return rightChild(index * 2) + 1 <= size;
}
static public void swap(int[] a, int index1, int index2){//swaps nodes
int temp = a[index1];
a[index1] = a[index2];
a[index2] = temp;
}
static public int peek(){//peeks at the top of the stack (min value)
return Heap[1];
}
public static boolean isEmpty(){
return size == 0;
}
static int randInt(int min, int max){//generates random int between two numbers
return ((int) (Math.random()*(max - min))) + min;
}
public String toString(){
String result = "[";
if(!isEmpty()){
result += Heap[1];
for(int i = 2; i <= size; i++){
result += ", " + Heap[i];
}
}
return result + "]";
}
public void add(int value){//adds the give value to this priority queue in order
if(size + 1 >= Heap.length){
Heap = Arrays.copyOf(Heap, Heap.length * 2);
}
size++;
Heap[size + 1] = value;//add as rightmost leaf
//"bubble up" as necessary to fix ordering
int index = size + 1;
boolean found = false;
while(!found && hasParent(index) && hasLeftChild(index)){
int parent = parent(index);
if(Heap[index] < Heap[parent]){
swap(Heap, index, parent(index));
index = parent(index);
}else{//after done bubbling up
found = true;
}
}
}
public int remove(){
//move rightmost leaf to become new root
int result = peek();//last leaf -> root
Heap[1] = Heap[size];
size--;
//"bubble down" as necessary to fix ordering
int index = 1;
boolean found = false;
while(!found && hasLeftChild(index)){
int left = leftChild(index);
int right = rightChild(index);
int child = left;
if(hasRightChild(index) && Heap[right] < Heap[left]){
child = right;
}
if(Heap[index] > Heap[child]){
swap(Heap, index, child);
index = child;
}else{
found = true;//found proper location, stop the loop
}
}
return result;
}
This is the code for my main class:
public static void main(String[] args){
Heap211 pq = new Heap211();
for(int node = 1;node <= 30; node++){//loop runs 30 times for 30 nodes
int smValue = randInt(0,2);//generates random number between 1 and 0
if(smValue == 0){//if random number is 0 then it will add random number to heap
int value = randInt(0,100);//generates random number between 0 and 100
System.out.println(node + " Add " + value + ": ");
pq.add(value);//adds random number
System.out.println(pq);//print heap
}else if(smValue == 1 && pq.isEmpty()){
int value = pq.remove();
System.out.println(node + " Remove " + value + ": ");
System.out.println(pq);
}
}
I have a GUI that displays all the numbers but I am getting the wrong output. Any helpful pointers would be greatly appreciated! Thanks.
I found a few problems in your code.
Your hasLeftChild function is wrong. You have return leftChild(index*2) <= size;. But you really should be checking for leftChild(index) <= size. You have a similar error in your hasRightChild function.
Not sure why you pass an array parameter to swap. The only array in which you swap stuff is the Heap array, which is a member of the class.
You have an error in your add method. You increment the size, and then add an item. That is:
size++;
Heap[size + 1] = value;
So imagine what happens when you add the first item. size is equal to 0, and you increment it to 1. Then you add the value at index size+1. So your array contains [0, 0, value]. That's probably the source of your extra 0's. I think what you want is:
Heap[size] = value;
size++;
You'll have to modify the rest of your code to take that into account.
Your "bubble up" loop is kind of wonky. You have:
while (!found && hasParent(index) && hasLeftChild(index))
That's never going to bubble anything up, because when you add something to the last element of the heap, that node doesn't have a left child. You also don't need the found flag. You can write:
while (hasParent(index) && Heap[index] < Heap[parent]]) {
swap(Heap, index, parent(index));
index = parent(index);
}
I can't guarantee that those are the only errors in your code, but they're the ones I found in a quick review of your code.
On a general note, why in the world are you creating a 1-based binary heap in a language that has 0-based arrays? There's no need to do that, and it's confusing as heck. For why I think it's a bad idea, see https://stackoverflow.com/a/49806133/56778 and http://blog.mischel.com/2016/09/19/but-thats-the-way-weve-always-done-it/.
Finally, you should learn to use your debugger, as suggested in comments. Take the time to do it now. It will save you hours of frustration.
So someone posted their solution to this, but I found that it didn't seem to work, I posted this there but I wanted to make it more accessible to others.
The question is in "Cracking the Code Interview" and it is the first tree question, feel free to make other suggestions (or prove me wrong!)
The key here is that it is difficult to keep track of the eventual paths and their heights with one stack.
What I ended up doing is pushing both the left and right child's height on a stack, checking if they are within one of one another, adding one to the max and then pushing that onto the stack after popping the left and right off.
I have commented so I hope it's clear enough
/* Returns true if binary tree with root as root is height-balanced */
boolean isBalanced(Node root) {
if(root == null) return false;
Deque<Integer> heights = new LinkedList<>();
Deque<Node> trail = new LinkedList<>();
trail.push(root);
Node prev = root; //set to root not null to not confuse when root is misisng children
while(!trail.isEmpty()) {
Node curr = trail.peek(); //get the next node to process, peek because we need to maintain trail until we return
//if we just returned from left child
if (curr.left == prev) {
if(curr.right != null) trail.push(curr.right); //if we can go right go
else {
heights.push(-1); //otherwise right height is -1 does not exist and combine heights
if(!combineHeights(heights)) return false;
trail.pop(); //back to parent
}
}
//if we just returned from right child
else if (curr.right == prev) {
if(!combineHeights(heights)) return false;
trail.pop(); //up to parent
}
//this came from a parent, first thing is to visit the left child, or right if no left
else {
if(curr.left != null) trail.push(curr.left);
else {
if (curr.right != null) {
heights.push(-1); //no left so when we combine this node left is 0
trail.push(curr.right); //since we never go left above logic does not go right, so we must here
}
else { //no children set height to 1
heights.push(0);
trail.pop(); //back to parent
}
}
}
prev = curr;
}
return true;
}
//pop both previous heights and make sure they are balanced, if not return false, if so return true and push the greater plus 1
private boolean combineHeights(Deque<Integer> heights) {
int rightHeight = heights.pop();
int leftHeight = heights.pop();
if(Math.abs(leftHeight - rightHeight) > 1) return false;
else heights.push(Math.max(leftHeight, rightHeight) + 1);
return true;
}
So in the end I managed to create an iterative solution which works for all test cases on Leetcode
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public static boolean isBalanced(TreeNode root) {
if (root == null) return true;
Deque<Pair> queue = new LinkedList<>();
queue.offer(new Pair(root, 0));
while (!queue.isEmpty()) {
var curr = queue.poll();
System.out.printf(">>Curr node is %s and curr.lvl is %s", curr.node.val, curr.lvl);
int left = getSubTreeHeight(new Pair(curr.node.left, curr.lvl + 1));
int right = getSubTreeHeight(new Pair(curr.node.right, curr.lvl + 1));
if (Math.abs(left - right) > 1) return false;
if (curr.node.left != null) queue.offer(new Pair(curr.node.left, curr.lvl + 1));
if (curr.node.right != null) queue.offer(new Pair(curr.node.right, curr.lvl + 1));
}
return true;
}
static int getSubTreeHeight(Pair pair) {
if (pair.node == null) {
return pair.lvl -1;
}
Deque<Pair> queue = new LinkedList<>();
queue.offer(pair);
int height = 0;
while (!queue.isEmpty()) {
Pair curr = queue.poll();
System.out.printf("Curr node is %s and curr.lvl is %s", curr.node.val, curr.lvl);
height = curr.lvl;
if (curr.node.left != null) queue.offer(new Pair(curr.node.left, curr.lvl + 1));
if (curr.node.right != null) queue.offer(new Pair(curr.node.right, curr.lvl + 1));
}
return height;
}
public static class Pair {
TreeNode node;
int lvl;
public Pair(TreeNode node, int lvl) {
this.node = node;
this.lvl = lvl;
}
}
}
The original question in the book does not mention the tree being binary. I happen to solve the same question, but coded in Python. So, here is my iterative solution for the problem, for general trees (where the children of a node is stored in a list), in python.
def is_balanced_nonrecursive(self):
stack = [self.root]
levels = [0]
current_min = sys.maxint
current_max = 0
current_level = 0
while len(stack) > 0:
n = stack.pop()
current_level = levels.pop()
for c in n.children:
stack.append(c)
levels.append(current_level + 1)
if len(n.children) == 0:
if current_level < current_min:
current_min = current_level
if current_level > current_max:
current_max = current_level
return current_max - current_min < 2
This is basically a depth first traversal of the tree. We keep a separate stack for the levels (the list levels). If we see any leaf node, we update the current min and current max levels accordingly. The algorithm traverses the whole tree and at the end if max and min levels differ by more than one, then the tree is not balanced.
There are many optimizations possible, like for instance checking whether the difference of min and max is more than one inside the loop, and if that is the case return False immediately.
Some code repetition on this one, but at least it doesn't give me a headache as the recursive ones do:
public boolean isBalanced() {
Queue<TreeNode> queue = new LinkedList<TreeNode>();
int leftLevel = 0;
int rightLevel = 0;
if(this == null) return false;
if(this.left != null)queue.offer(this.left);
while(!queue.isEmpty()) {
int size = queue.size();
for(int i=0; i < size; i++) {
if(queue.peek().left != null) queue.offer(queue.peek().left);
if(queue.peek().right != null) queue.offer(queue.peek().right);
queue.poll();
}
leftLevel++;
}
if(this.right != null) queue.offer(this.right);
while(!queue.isEmpty()) {
int size = queue.size();
for(int i=0; i < size; i++) {
if(queue.peek().left != null) queue.offer(queue.peek().left);
if(queue.peek().right != null) queue.offer(queue.peek().right);
queue.poll();
}
rightLevel++;
}
return Math.abs(leftLevel - rightLevel) < 2;
}
I have a trie where each node is an object TrieNode like this:
public char content;
public double count;
public LinkedList<TrieNode> childList;
I had to count the height of the trie (root had level = 0).
So this is what I've done:
int levels = getLevels(getRoot());
System.out.println("levels: " + levels);
public int getLevels(TrieNode node) {
int lev = 0;
if(node != null) {
TrieNode current = node;
for(TrieNode child : node.childList) {
lev += getLevels(child);
}
}
return lev;
}
But it returns always 0. Why?
Thanks
You need to add 1 when you descend to children, otherwise nothing gives lev a non-zero value.
Note that you're not calculating the height of the trie in this code, you're summing the lengths of the paths. You need to find the maximum path length:
int lev = 1;
for (TrieNode child : node.childList) {
lev = Math.max(lev, 1 + getLevels(child));
}