I'm trying to write a method for the maximum sum of a path through a binary tree:
public class ConsTree<T> extends BinaryTree<T>
{
BinaryTree<T> left;
BinaryTree<T> right;
T data;
public int maxSum()
{
}
}
As is shown, each tree contains a tree to its left and to its right, as well as a data of a generic type. I am somewhat confused on how to go about starting this. If someone could provide help as to what the algorithm might look like or put me in the right direction, that would be great. Thanks.
The way to think recursively is to consider the cases. In our case, we look at a single node and can decide what paths it has:
If the node has no children, then the only path is the singleton path (consisting of that node by itself).
If the node has only one child, then all paths go through that child.
Otherwise, the node has two children. Then, all paths go through one of its two children.
In case 1, the maximum must be that node's value. In case 2, the maximum is that node's value, plus the max-path-sum of its child (since that path is extended to a path for the parent through the only child). In case 3, the maximum is the maximum max-path-sum of its two children (since the best path must go through one of the two children, and the parent can see which of the children's best paths is better).
Therefore, the code is really simple. Here, since you return an int, I'm going to assume T = int.
public int maxSum() {
/* case 1 */
if(left == null && right == null)
return data;
/* case 2 */
if(right == null)
return data + left.maxSum();
else if(left == null)
return data + right.maxSum();
/* case 3 */
return Math.max(data + left.maxSum(), data + right.maxSum());
}
private static int max = Integer.MIN_VALUE;
public static int maxPathSum(TreeNode root) {
int rd = dfs(root);
return rd > max ? rd : max;
}
// close paths:
// 1 left leaf
// 2 right leaf
// 3 left + root + right
//
// open paths:
// 4 root
// 5 left + root
// 6 right + root
private static int dfs(TreeNode root) {
if (root.left == null && root.right == null) {
return root.val;
}
if (root.left == null && root.right != null) {
int rPathMax = dfs(root.right);
max = rPathMax > max ? rPathMax : max;
return Math.max(root.val, rPathMax + root.val);
}
if (root.right == null && root.left != null) {
int lPathMax = dfs(root.left);
max = lPathMax > max ? lPathMax : max;
return Math.max(root.val, lPathMax + root.val);
}
int lPathMax = dfs(root.left);
int rPathMax = dfs(root.right);
int closePathMax = lPathMax + rPathMax + root.val;
int innerMax = Math.max(closePathMax, Math.max(lPathMax, rPathMax));
max = innerMax > max ? innerMax : max;
return Math.max(root.val, Math.max(lPathMax + root.val, rPathMax + root.val));
}
Related
In my program, I have initialized a binary tree and I want to search if a node exists in that binary tree. If it does exist, then I will print the subtree of that node and the level it was found. I am able to perform my search method but I am not sure how to print the subtree of that node found and its level.
For example, this is my binary tree [K=3 L=[K=1 R=[K=2]] R=[K=5 L=[K=4]]. If I search for node 1, then it will return the node (not null) since it exists in my binary tree.
Problem: I need to print only the subtree of the node and the level where it was found: [K=1 R=[K=2]], level=1.
Here is my source code for reference:
Main Class
// instantiate BST object
BST<Character> bst = new BST<>();
// insert values to bst1
String str = "31254";
char[] letter = str.toCharArray();
for (int i = 0; i < letter.length; i++) {
Character c = letter[i];
bst1.insert(c);
}
// print bst
System.out.println("\nht=" + bst1.height + " " + bst1.toString() + "\n");
// search values in bst1
String str2 = "016483";
char[] letter2 = str2.toCharArray();
for (int i = 0; i < letter2.length; i++) {
Character c = letter2[i];
if (bst1.search(c) != null) { // if found, print the node (w/ subtree) and its level
// ** part that should print the subtree and its level
}
else {
System.out.println(c + " is not found.");
}
}
BST Class - where my search method is declared
public class BST<T extends Comparable<T>> extends BT<T> {
// insert() method
// search method
public BTNode<T> search(T k) {// my method
BTNode<T> n = root;
while (n != null) {
if (n.info.compareTo(k) == 0) {
return n;
}
else {
if (n.info.compareTo(k) > 0) {
n = n.left;
}
else {
n = n.right;
}
}
}
return null;
}
}
Thanks in advance!
I did not modify your code. Instead, I used an imaginary class Node that I've written. Also, I have written all of them as half pseudo half java code.
Suppose your node has only one int type variable and two children.
class Node{
int data;
Node left;
Node right;
}
You have a printExistingSubTree method in your BST class that does the what you exactly ask for:
printExistingSubTree(Node node, int key):
if (find(node.left, key, 0) != -1)
then
printTree(node.left)
print(find(node.left, key, 0))
if (find(node.right, key, 0) != -1)
then printTree(node.right)
printTree(node.right)
print(find(node.right, key, 0))
You have a find method in your BST class that does find the index:
int find(Node node,int key, int level)
if(node.data is equal to the key)
then return level
int left = -1;
if(node.leftChild is not null)
then left = find(node.left, key, level+1)
if(left != -1)
return left
int right = -1;
if(node.rightChild is not null)
then right = find(node.right, key, level+1)
return right
Then, to print, you should decide how you want to traverse the subtree.
You have a printTree method in your BST class that prints the subtree in postorder:
void printTree(Node node){
if (node == null)
return;
printTree(node.left);
printTree(node.right);
print(node.data + " ");
}
Note: I did not understand your whole code. Therefore, I have just written the answer of your question in the pseudo code format. I think, you can write your own code from that.
Note2: There may be some typos&wrong namings. PLease do not lynch. Just write in the comments and I'll correct.
I am having trouble finding out if the number of nodes at each level is the same. The question and my code so far is provided below
Two binary trees are called similar sized if the number of nodes is the same at each level of the tree
Given the following:
class TreeNode {
String nodeValue;
TreeNode rightNode;
TreeNode leftNode;
TreeNode(String nodeValue, TreeNode rightNode, TreeNode leftNode) {
this.nodeValue = nodeValue;
this.rightNode = rightNode;
this.leftNode = leftNode;
}
}
The goal of this question is to write a function that will validate if two trees are similar sized.
The function should return true if this is correct and false otherwise
my code:
//implemented with java
class TreeNode {
String nodeValue;
TreeNode rightNode;
TreeNode leftNode;
TreeNode(String nodeValue, TreeNode rightNode, TreeNode leftNode) {
this.nodeValue = nodeValue;
this.rightNode = rightNode;
this.leftNode = leftNode;
}
//function to return size of node, i.e. number of children
int nodeSize() {
//if node has both left and right child node
if (this.rightNode.nodeValue != null && this.leftNode.nodeValue != null) {
return 2;
//if node has no child nodes
} else if (this.rightNode.nodeValue == null && this.leftNode.nodeValue == null) {
return 0;
//if node just has either left or right child node
} else {
return 1;
}
}
boolean similarSizedTrees(TreeNode firstTree, TreeNode secondTree) {
//if both nodes have no child nodes
if (firstTree.nodeSize() == 0 && secondTree.nodeSize() == 0) {
return true;
}
//if both nodes have at least 1 child node
if (firstTree.nodeSize() != 0 && secondTree.nodeSize() != 0) {
return ((firstTree.nodeSize() == secondTree.nodeSize()) &&
similarSizedTrees(firstTree.leftNode, secondTree.leftNode) &&
similarSizedTrees(firstTree.leftNode, secondTree.rightNode));
}
//
return false;
}
}
What I am having trouble with is my code does not account for the number of nodes at each level.
Your recursion based approach wouldn't work, because the number of nodes in the next level aren't influenced by which node each child descends from. You could modify your approach to use a simple level order traversal.
(Edit: adding #Piotr's explanation)
The basic idea is to count nodes on each level. In the example picture there is 1 node on level 1, 2 nodes on level 2 and 3 nodes on level 3 and finally 1 node on level 4. These numbers are exactly the same for both tree, even if they are not exactly the same. This algorithm is computing these counts for each level, and if discrepancy is found, it returns False. Otherwise it returns True in the end if both trees are similar. (I'm not well versed in Java, but you can easily translate the algorithm):
def similar(firstTree, secondTree):
queue1, queue2 = queue(firstTree), queue(secondTree) # create queues for traversal
# each queue stores all nodes in a given level
while queue1 and queue2:
if len(queue1)!=len(queue2): # check base condition
return False
i = 0
while i<len(queue1): # append all next level nodes for tree 1
node = queue1.pop()
if node.left:
queue1.insert(node.left)
if node.right:
queue1.insert(node.right)
i += 1
i = 0
while i<len(queue2): # append all next level nodes for tree 2
node = queue2.pop()
if node.left:
queue2.insert(node.left)
if node.right:
queue2.insert(node.right)
i += 1
if queue1 or queue2:
return False # either tree couldn't complete traversal because of different heights
return True
A couple solutions can be found here: https://www.techiedelight.com/check-if-two-binary-trees-are-identical-not-iterative-recursive/
this is the recursive function shown from the link mentioned above tailored to your code.
boolean similarSizedTrees(TreeNode x, TreeNode y) {
// bottom of tree reached
if (x == null && y == null) {
return true;
}
// both trees are non-empty and the value of their root node matches,
// recur for their left and right subtree
return (x != null && y != null) && (x.key == y.key) &&
similarSizedTrees(x.left, y.left) &&
similarSizedTrees(x.right, y.right);
}
EDIT: You could also keep count of the nodes at each level using two global int variables, one for each tree, as you traverse the tree increment each variable, then compare them together before continuing down to the next level.
Here is a solution using recursion:
int find_height(TreeNode t){
return (t == null)? 0 : 1 + Math.max(find_height(t.leftNode), find_height(t.rightNode));
}
boolean is_similar_tree(TreeNode l, TreeNode r){
int l_height = find_height(l);
int r_height = find_height(r);
if(l_height != r_height)
return false;
int[] l_height_sums = new int[l_height];
int[] r_height_sums = new int[r_height];
is_similar_tree_helper(l, l_height_sums, l_height);
is_similar_tree_helper(r, r_height_sums, r_height);
return Arrays.equals(l_height_sums, r_height_sums);
}
void is_similar_tree_helper(TreeNode t, int[] table, int height){
if(t == null || height == 0)
return;
table[height - 1]++;
is_similar_tree_helper(t.leftNode, table, height - 1);
is_similar_tree_helper(t.rightNode, table, height - 1);
}
The idea is to count the number of nodes in each level, then check if the total number of levels of two trees are the same or not. If it is not the same then return false otherwise check each level count and compare the value. If two values are equal then return true otherwise false.
class TreeNode {
String nodeValue;
TreeNode rightNode;
TreeNode leftNode;
TreeNode(String nodeValue, TreeNode rightNode, TreeNode leftNode) {
this.nodeValue = nodeValue;
this.rightNode = rightNode;
this.leftNode = leftNode;
}
public void countLevelNode(TreeNode root, int level, int[] sum) {
if(root == null) return;
sum[level]++;
countLevelNode(root.leftNode, level + 1, sum);
countLevelNode(root.rightNode, level + 1, sum);
}
boolean similarSizedTrees(TreeNode firstTree, TreeNode secondTree) {
int[] countFirstTreeNode = new int[1000];
int[] countSecondTreeNode = new int[1000];
int totalLevelofFirstTree = 0;
int totalLevelofSecondTree = 0;
countLevelNode(firstTree, totalLevelofFirstTree, countFirstTreeNode);
countLevelNode(secondTree, totalLevelofSecondTree, countSecondTreeNode);
if(totalLevelofFirstTree != totalLevelofSecondTree){
return false;
}
else{
for(int i = 0; i < totalLevelofFirstTree; i++){
if(countFirstTreeNode[i] != countSecondTreeNode[i]){
return false;
}
}
return true;
}
}
}
I'm looking at two solutions to find the first common ancestor of two nodes in a binary tree (not necessarily a binary search tree). I've been told the second solution provides a better worst-case run time, but I can't figure out why. Can someone please enlighten me?
Solution 1:
Find the depth of each of the two nodes: p,q
Calculate the delta of their depths
Set a pointer at the shallower node a pointer a the deeper node
Move the deeper node pointer up by the delta so we can start traversing from the same height
Recursively visit the part nodes of both pointers until we arrive at the same node which is out the first common ancestor
import com.foo.graphstrees.BinaryTreeNodeWithParent;
/*
Find the first common ancestor to 2 nodes in a binary tree.
*/
public class FirstCommonAncestorFinder {
public BinaryTreeNodeWithParent find(BinaryTreeNodeWithParent p, BinaryTreeNodeWithParent q) {
int delta = depth(p) - depth(q);
BinaryTreeNodeWithParent first = delta > 0 ? q: p; // use shallower node
BinaryTreeNodeWithParent second = delta > 0 ? p: q; //use deeper
second = goUp(second, delta); // move up so they are level, if 1 node is deeper in the tree than the other, their common ancestor obviously cannot be below the shallower node, so we start them off at the same height in the tree
//keep going up the tree, once first == second, stop
while(!first.equals(second) && first !=null && second !=null) {
first = first.getParent();
second = second.getParent();
}
return first == null || second == null ? null : first;
}
private int depth(BinaryTreeNodeWithParent n) {
int depth = 0;
while (n != null) {
n = n.getParent();
depth++;
}
return depth;
}
private BinaryTreeNodeWithParent goUp(BinaryTreeNodeWithParent node, int delta) {
while (delta > 0 && node != null) {
node = node.getParent();
delta--;
}
return node;
}
}
Solution 2:
Verify both nodes (p,q) exist in the tree starting at the root node
Verify that q is not a child of p and p is not a child of q by traversing their subtrees
Recursively examine subtrees of successive parent nodes of p until q is found
import com.foo.graphstrees.BinaryTreeNodeWithParent;
public class FirstCommonAncestorImproved {
public BinaryTreeNodeWithParent find(BinaryTreeNodeWithParent root,
BinaryTreeNodeWithParent a,
BinaryTreeNodeWithParent b) {
if (!covers(root, a) || !covers(root, b)) {
return null;
} else if (covers(a, b)) {
return a;
} else if (covers(b, a)) {
return b;
}
var sibling = getSibling(a);
var parent = a.getParent();
while (!covers(sibling, b)) {
sibling = getSibling(parent);
parent = parent.getParent();
}
return parent;
}
private BinaryTreeNodeWithParent getSibling(BinaryTreeNodeWithParent node) {
if (node == null || node.getParent() == null) return null;
var parent = node.getParent();
return node.equals(parent.getLeft()) ? node.getRight() : node.getLeft();
}
private boolean covers(BinaryTreeNodeWithParent root,
BinaryTreeNodeWithParent node) {
if (root == null) return false;
if (root.equals(node)) return true;
return covers(root.getLeft(), node) || covers(root.getRight(), node);
}
}
It depends on the structure of the problem.
If the starting nodes are deep in a big tree, and the ancestor is close by, then the first algorithm will still need to traverse the entire path to the root to find the depths. The second will succeed by inspecting only a small subtree.
On the other hand, if the nodes are deep and the common ancestor is very near the root, then the first will only traverse two paths to the root, while the second may explore the entire tree.
Note that as is often the case you can get an asymptotically faster algorithm by trading space for speed. Maintain a set of nodes. Traverse upward in alternating steps from both starting nodes, adding to the set until you find one that's already there. That's the common ancestor. Given the set operations are O(1), this algorithm is O(k) where k is the path length from the common ancestor to the most distant start node. You can't do better.
Set<Node> visited = new HashSet<>();
while (a != null && b != null) {
if (visited.contains(a)) return a;
if (visited.contains(b)) return b;
visited.add(a);
visited.add(b);
a = a.parent();
b = b.parent();
}
while (a != null) {
if (visited.contains(a)) return a;
a = a.parent();
}
while (b != null) {
if (visited.contains(b)) return b;
b = b.parent();
}
return null;
THIS IS HOMEWORK, DO NOT POST CODE. Please and thank you.
I have been assigned to create a method that calculates the depth of a particular in a BST.
To do this, I am to #Override a method public int depth(T data). So, to find it recursively, I need to create a helper method.
I know that I need to essentially search the tree for the node with the data I'm looking for. So, to do this, I've written the following code:
#Override
public int depth(T data) {
if (data == null) {
throw new IllegalArgumentException("Data is null");
}
if (compare(data, root.getData()) == 0) {
return 1;
} else {
return countNodes(search(root, data), data);
}
}
private int countNodes(BSTNode<T> node, T data) {
int depth = 1;
if (node == null) {
return 0;
} else {
if (compare(data, node.getData()) == 0) {
return depth;
} else if (compare(data, node.getData()) < 0) {
return countNodes(node.getLeft(), data);
} else {
return countNodes(node.getRight(), data);
}
}
}
private int compare(T a, T b) {
return a.compareTo(b);
}
However, this isn't going to work, as depth would remain 1 each time the recursive call is made; essentially, it's resetting the value of depth. I have no idea how to keep the value of depth in-between calls.
Your depth method should return one if the current node is the one you are looking for or the depth of the next node down plus one if you need to keep looking.
For example:
N5
N3 N7
N1 N4 N6
You want to know the depth of N1.
You start at the root:
N1 != N5 so you return one plus the result of your check on N3. (1 + (result of N3 check))
N1 != N3 so you return one plus the result of your check on N1. (1 + (1 + (result of N1 check)))
N1 == N1 so you you return one. (1 + (1 + (1)))
(1 + (1 + (1))) = 3
this is the correct depth and is what the initial depth call will return.
Many answers on the net for 'finding Least Common Ancestor in binary tree' and its supplementary question 'find distance between 2 nodes' have 4 issues:
Does not consider duplicates
Does not consider if input node is invalid/absent/not in tree
Use extra / aux storage
Not truncating the traversal although answer is obtained.
I coded this sample to overcome all handicaps. but since I did not find 'a single' answer in this direction, I would appreciate if my code has a significant disadvantage which I am missing. Maybe there is none. Additional eyeballs appreciated.
public int distance(int n1, int n2) {
LCAData lcaData = new LCAData(null, 0, 0);
int distance = foundDistance (root, lcaData, n1, n2, new HashSet<Integer>());
if (lcaData.lca != null) {
return distance;
} else {
throw new IllegalArgumentException("The tree does not contain either one or more of input data. ");
}
}
private static class LCAData {
TreeNode lca;
int count;
public LCAData(TreeNode parent, int count) {
this.lca = parent;
this.count = count;
}
}
private int foundDistance (TreeNode node, LCAData lcaData, int n1, int n2, Set<Integer> set) {
assert set != null;
if (node == null) {
return 0;
}
// when both were found
if (lcaData.count == 2) {
return 0;
}
// when only one of them is found
if ((node.item == n1 || node.item == n2) && lcaData.count == 1) {
// second element to be found is not a duplicate node of the tree.
if (!set.contains(node.item)) {
lcaData.count++;
return 1;
}
}
int foundInCurrent = 0;
// when nothing was found (count == 0), or a duplicate tree node was found (count == 1)
if (node.item == n1 || node.item == n2) {
if (!set.contains(node.item)) {
set.add(node.item);
lcaData.count++;
}
// replace the old found node with new found node, in case of duplicate. this makes distance the shortest.
foundInCurrent = 1;
}
int foundInLeft = foundDistance(node.left, lcaData, n1, n2, set);
int foundInRight = foundDistance(node.right, lcaData, n1, n2, set);
// second node was child of current, or both nodes were children of current
if (((foundInLeft > 0 && foundInRight > 0) ||
(foundInCurrent == 1 && foundInRight > 0) ||
(foundInCurrent == 1 && foundInLeft > 0)) &&
lcaData.lca == null) {
// least common ancestor has been obtained
lcaData.lca = node;
return foundInLeft + foundInRight;
}
// first node to match is the current node. none of its children are part of second node.
if (foundInCurrent == 1) {
return foundInCurrent;
}
// ancestor has been obtained, aka distance has been found. simply return the distance obtained
if (lcaData.lca != null) {
return foundInLeft + foundInRight;
}
// one of the children of current node was a possible match.
return (foundInLeft + foundInRight) > 0 ? (foundInLeft + foundInRight) + 1 : (foundInLeft + foundInRight);
}
The algorithm appears to be (without pulling it apart entirely) to exhaustively traverse the entire tree until a node is found where there is one node found on the left and one on the right. And creating an additional set as you go.
The problem here seems to be that your algorithm is very inefficient. That may fit your requirements, if this particular operation is almost never carried out. But normally you could do better.