I am currently coding a recursive method to return the max imbalance on the whole of a Binary Search Tree. I've very new to recursive programming so it's quite difficult to wrap my head around. The tree I have built has an Imbalance of 1 but my method only returns 0. I'm sure my logic here is flawed.
I'm 100% sure its running " (root == null){ return 0;} " in every step of the method. I tried removing it and defining it further and it continues to do the same.
This is my current method:
public int getMaxImbalance(){
return Math.abs(getMaxImbalance(root));
}
public int getMaxImbalance (TreeNode<E> root){
if (root == null){
return 0;
}else if(root.left != null && root.right == null){
return 1 + getMaxImbalance(root.left) + getMaxImbalance(root.right);
//adds 1 left is true and right is false
}else if(root.left == null && root.right != null){
return -1 + getMaxImbalance(root.left) + getMaxImbalance(root.right);
//adds -1 left is false and right is true
}
return getMaxImbalance(root.left) + getMaxImbalance(root.right);
//calls itself if both fields are null;
}
The logic in your code seems wrong: the max imbalance of a node is not the sum of the max imbalance of its child(ren). Rather, the max imbalance should be the abs of the difference of the height of its child(ren) (if one of them is empty the max imbalance of that node is just 0, so the max imbalance of the current node depends entirely on it's only child).
Related
Here is my attempt to check whether a tree is a BST or not:
public boolean isBST() {
return isBSTRecursively(this.root, new Max());
}
class Max {
int value;
}
private boolean isBSTRecursively(Node node, Max max) {
if (node == null) return true; // to handle null root
// to handle scenario when both child nodes are absent
if(node.getLeft() == null && node.getRight() == null) {
max.value = node.getValue();
return true;
}
// if left child is absent, we only investigate right subtree
if(node.getLeft() == null) {
Max rightMax = new Max();
boolean isRightBST = isBSTRecursively(node.getRight(), rightMax);
max.value = Math.max(node.getValue(), rightMax.value);
if(isRightBST && node.getValue() < rightMax.value) {
return true;
} else {
return false;
}
} else {
Max leftMax = new Max();
boolean isLeftBST = isBSTRecursively(node.getLeft(), leftMax);
// if right child is absent, we only investigate left subtree
if(node.getRight() == null) {
max.value = Math.max(node.getValue(), leftMax.value);
if(isLeftBST && node.getValue() > leftMax.value) {
return true;
} else {
return false;
}
} else {
// we investigate both left and right subtrees
Max rightMax = new Max();
boolean isRightBST = isBSTRecursively(node.getRight(), rightMax);
max.value = Math.max(Math.max(leftMax.value, node.getValue()), rightMax.value);
if(isLeftBST && isRightBST && leftMax.value < node.getValue() && node.getValue() < rightMax.value) {
return true;
} else {
return false;
}
}
}
Code works fine as tested with multiple test cases.
But I am not sure if this is a good, clean approach.
Recursive method is big it seems. I am dealing with scenarios like null left node, null right node, node itself null, both child nodes null etc. separately. I guess they all can be handled in a much smaller, and cleaner way.
Moreover, I am always more inclined towards iterative approach(I generally find it better to visualize). Would that be better here (given it can be done iteratively)?
Any suggestions?
A cleaner recursive approach
You could use a bounded approach, i.e. have two variables for every recursion: min and max.
Initially min = INT_MIN and max = INT_MAX
if node = NULL then return True because an empty BST is a BST
else check if node.val < min or node.val > max if this condition is True then tree is not a BST, return False Notice : the strict inequality > and < are used as BST doesn't allow duplicate elements.
recurse for left : recur(node.left) with min remaining the same and max = node.val - 1 because the left subtrees should have values not greater than node.val - 1.
The max cannot be node.val because BST cannot have duplicate elements.
Store the boolean return value in say left
recurse for right : recur(node.right) with min = node.val + 1 and max remaining the same.
The right subtrees should have values not less than node.val + 1.
Store the boolean return value in say right
return left && right
#Aditya gave the ingredients of the more elegant solution.
It is generally not forbidden for a binary search tree to have duplicate values, so there should no be reduction of the "window" with 1.
Here is suggested code:
public boolean isBST() {
return isBSTRecursively(this.root, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
private boolean isBSTRecursively(Node node, int min, int max) {
return node == null
|| node.getValue() >= min && node.getValue() <= max
&& isBSTRecursively(node.getLeft(), min, node.getValue())
&& isBSTRecursively(node.getRight(), node.getValue(), max);
}
Ok so I am working on my algorithm and data structure knowledge and I am trying to find the largest number in each level of a binary tree. I am not sure what is wrong with my logic here.
class Solution
{
int maxLevel = 0;
public ArrayList<Integer> largestValues(Node root)
{
//code here
ArrayList<Integer> arr = new ArrayList<>();
checkHeight(root, 1, arr);
return arr;
}
void checkHeight(Node root, int height, ArrayList<Integer> arr){
if(root == null){
return;
}
if(maxLevel < height){
if(root.left != null && root.right != null){
arr.add(Math.max(root.left.data, root.right.data));
}
if(root.left != null && root.right == null){
arr.add(root.left.data);
}
if(root.left == null && root.right != null){
arr.add(root.right.data);
}
if(root.left == null && root.right == null){
return;
}
maxLevel = height;
}
checkHeight(root.left, height + 1, arr);
checkHeight(root.right, height + 1, arr);
}
}
It appears that you are not familiar with what largest number in each level of a binary tree means. Your current implementation takes the largest of the left and right children of a node and if it only has one child then that child.
A level in a binary tree consists of all the nodes that have the same depth regardless of whether they are immediate children of the same parent or not.
Therefor you algorithm is fundamentally wrong.
There are 2 approaches to solve this problem: DFS and BFS.
BFS is more straight forward, or it appears to me at least. Here's how it goes:
Loop over the nodes level by level (which BFS does by design).
Take the maximum element.
Move to the next level.
DFS works as follows:
Keep a dictionary that maps levels as keys to greatest number in the level as value.
As you dive dfs, keep track of the depth with a parameter to your recursive function.
At each node, update the value of the key for that level if the current node is greater.
At the end convert your dictionary to whatever format you need.
Note: It doesn't matter what flavor of DFS you use (inorder, preorder, etc.). Just make sure to visit every node and keep track of the depth.
Trying to solve this LC Easy: https://leetcode.com/problems/minimum-depth-of-binary-tree/
Which is to find the minimum depth (number of nodes on shortest path) of a tree.
I was able to create a "findheight" function which gives me the height of a tree.
My logic was to use findheight to find the height of both subtrees (left and right) of a root node, and then return the minimum between the two heights.
class Solution {
public int minDepth(TreeNode root) {
if(root == null){return 0;}
int left = findHeight(root.left);
int right = findHeight(root.right);
//unbalanced tree, only one subtree
if(left == 0 || right == 0){
return Math.max(left,right) + 1;
}
return Math.min(left,right) + 1 ;
}
public int findHeight(TreeNode root){
if(root == null){return 0;}
int left = findHeight(root.left);
int right = findHeight(root.right);
return Math.max(left,right) + 1;
}
}
It won't pass the test case:
[-9,-3,2,null,4,4,0,-6,null,-5]
Or:
Output:
4
Expected:
3
My thought process right now is that when I use "findHeight", I'm returning back the 'max' height per left and right subtree. In this test case, I should be returning back the minimum height.
I changed my code to "Math.min" in another iteration, but that doesn't work either.
Any ideas or theories why? So confused!! Should I just abandon this approach altogether?
Issue in current code
//unbalanced tree, only one subtree
if(left == 0 || right == 0){
return Math.max(left,right) + 1;
}
The above lines of code checks the imbalance only at root level. It does not recursively check imbalance at lower levels.
Check imbalance at every level
public int minDepth(final TreeNode node) {
if (node == null) {
return 0;
}
final int left = minDepth(node.left);
final int right = minDepth(node.right);
// if both paths exist, then return the minimum
if (node.left != null && node.right != null) {
return Math.min(left, right) + 1;
} else {
// if zero or one path exists return that path (so take maximum)
return Math.max(left, right) + 1;
}
}
I want to convert a sorted integer array into a binary search tree. I have posted my code below. What I cannot picture is how the recursion actually works with the for loop as inserting.
So if my array is [1,3,4, 5,8,10] I make 4, which is the mid of the array, become the root of my BST, then loop from the start of array and insert to the tree with root just created. My question is why the order of result inserted is not as the sorted given array?
public TreeNode sortedArrayToBST(int[] A) {
if (A == null || A.length == 0){
return null;
}
TreeNode root = new TreeNode(findMid(A));
for (int i = 0; i < A.length; ++i){
insert(root, A[i]);
}
return root;
}
private int findMid(int[] A){
int left = 0;
int right = A.length -1;
int mid = A[left + (right - left)/2];
return mid;
}
private void insert (TreeNode root, int val){
if (root == null || root.val == val){
return;
}
if (val < root.val){
TreeNode left = new TreeNode(val);
root.left = left;
}
if (val > root.val){
TreeNode right = new TreeNode(val);
root.right = right;
}
insert(root.left,val);
insert(root.right,val);
}
You have a couple problems with your recursive insert method. First off, everytime the val is not equal to the root's value, you create a new node. This is faulty because by doing this, you create multiple nodes and set the root's child at each step of the recursion to these new nodes, which is redundant. Let's go through your method for each node.
Adding 4
4
Adding 1
4
/
1
Adding 3
4
/
3
At this point, we can pinpoint the error. Why was 4's left child replaced with 3? Let's go through your insert method where root is the node with value 4 and val is 3.
First if-statement condition evaluates to false, so move on
Second if-statement condition evaluates to true, so create a new node with val and set root.left equal to this new node
Third if-statement condition evaluates to false, so move on
Recursive call insert(3.left, 3) just returns since 3 == 3
Recursive call insert(null, 3) just returns since root == null
So what's the fix? STOP creating new nodes at every recursive call in the call stack. Believe it or not, you should only be creating a new node when root is null, because this signifies that you've traversed the tree down to an empty child. What about the recursive calls? There's no need to do a recursive call on each of the root's children because you only go down one traversal path in a BST. You either turn left or right at each node. So what you do is only make a recursive call depending on the value of val relative to the root's value. Here's what it should look like,
private TreeNode insert (TreeNode root, int val){
if (root == null){
return new TreeNode(val);
}
if (val == root.val){
//if you don't want to add repeats in the tree, then
//add your own code to deal with that here
//although as it stands now, this code will not add repeats
}
if (val < root.val){
root.left = insert(root.left, val);
}
if (val > root.val){
root.right = insert(root.right, val);
}
return root;
}
I would like to calculate the summation of the depths of each node of a Binary Search Tree.
The individual depths of the elements are not already stored.
Something like this:
int countChildren(Node node)
{
if ( node == null )
return 0;
return 1 + countChildren(node.getLeft()) + countChildren(node.getRight());
}
And to get the sum of the depths of every child:
int sumDepthOfAllChildren(Node node, int depth)
{
if ( node == null )
return 0; // starting to see a pattern?
return depth + sumDepthOfAllChildren(node.getLeft(), depth + 1) +
sumDepthOfAllChildren(node.getRight(), depth + 1);
}
Now for a hopefully informative explanation in case this is homework. Counting the number of nodes is quite simple. First of all, if the node isn't a node (node == null) it returns 0. If it is a node, it first counts its self (the 1), plus the number of nodes in its left sub-tree plus the number of nodes in its right sub-tree. Another way to think of it is you visit every node via BFS, and add one to the count for every node you visit.
The Summation of depths is similar, except instead of adding just one for each node, the node adds the depth of its self. And it knows the depth of its self because its parent told it. Each node knows that the depth of it's children are it's own depth plus one, so when you get the depth of the left and right children of a node, you tell them their depth is the current node's depth plus 1.
And again, if the node isn't a node, it has no depth. So if you want the sum of the depth of all the root node's children, you pass in the root node and the root node's depth like so: sumDepthOfAllChildren(root, 0)
Recursion is quite useful, it's just a very different way of thinking about things and takes practice to get accustomed to it
int maxDepth(Node node) {
if (node == null) {
return (-1); // an empty tree has height −1
} else {
// compute the depth of each subtree
int leftDepth = maxDepth(node.left);
int rightDepth = maxDepth(node.right);
// use the larger one
if (leftDepth > rightDepth )
return (leftDepth + 1);
else
return (rightDepth + 1);
}
}
This solution is even more simpler.
public int getHeight(Node root)
{
if(root!=null)
return 1+ Math.max(getHeight(root.leftchild),getHeight(root.rightchild));
else
return 0;
}
For any given tree, the number of nodes is 1 for the root plus the number of nodes in the left subtree plus the number of nodes in the right subtree :)
Details, like making sure there actually is a left or right subtree, are "left to the reader".
private static int getNumberOfNodes(Node node) {
if (node == null) {
return 0;
}
return 1 + getNumberOfNodes(node.left) + getNumberOfNodes(node.right);
}
public int countNodes(Node root)
{
// Setup
// assign to temps to avoid double call accessors.
Node left = root.getLeft();
Node right = root.getRight();
int count = 1; // count THIS node.
// count subtrees
if (left != null) count += countNodes(left);
if (right != null) count += countNodes(right);
return count;
}
public class Node {
private Node left;
private Node right;
public int size() { return 1+ (left==null?0:left.size())+ (right==null?0:right.size());}
}
int depth(treenode *p)
{
if(p==NULL)return(0);
if(p->left){h1=depth(p->left);}
if(p=>right){h2=depth(p->right);}
return(max(h1,h2)+1);
}
public int numberOfNodes()
{
// This node.
int result = 1;
// Plus all the nodes from the left node.
Node left = getLeft();
if (left != null)
result += left.numberOfNodes();
// Plus all the nodes from the right node.
Node right = getRight();
if (right != null)
result += right.numberOfNodes();
return result;
}
public int getDepthHelper( TreeNode< T > node ) {
int treeHeightLeft;
int treeHeightRight;
//get height of left subtree
if( node.leftNode == null )
treeHeightLeft = 1;
else {
treeHeightLeft = getDepthHelper( node.leftNode) + 1;
}
//get height of right subtree
if( node.rightNode == null )
treeHeightRight = 1;
else {
treeHeightRight = getDepthHelper( node.rightNode) + 1;
}
return Math.max(treeHeightLeft, treeHeightRight);
}