Determine if 2 binary trees are similar - java

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;
}
}
}

Related

Why does this common ancestor solution have better worst-case performance?

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;

Similiar functions to traverse a binary tree

I have got a binary tree
public class Node
{
int value;
Node left;
Node right;
public Node getLeft() {
return left;
}
public Node getRight() {
return right;
}
public String getValue() {
return value;
}
}
And in main I have got a function to traverse it.
For tree
5
/ \
3 7
/ \
1 2
First one creates a queue of nodes with breadth first traversal(5,3,7,1,2).
Second one returns value of a node for eg. 7 for number 2 or 2 for number 4.
private void queueOfTreaversed() {
LinkedList<Node> queue = new LinkedList<Node>();
if (root != null)
queue.add(root);
while (!queue.isEmpty()) {
Node temp = queue.removeFirst();
if (temp.getLeft() != null && temp.getRight() != null) {
traversed.add(temp); //there is a difference
queue.add(temp.getLeft());
queue.add(temp.getRight());
}
}
}
public int getValue(int n) {
LinkedList<Node> queue = new LinkedList<Node>();
if (root != null)
queue.add(root);
while (!queue.isEmpty() && n>0) {
Node temp = queue.removeFirst();
if (temp.getLeft() != null && temp.getRight() != null) {
queue.add(temp.getLeft());
queue.add(temp.getRight());
}
}
return queue.peekFirst().getValue(); //there is a difference
}
And I have got duplication of code that I do not how to get rid off.
I use traversed in meantime and pop elements from this queue so elements will not be in this order and traversed cannot be used. Could anyone give any hint?
Once you have got the traversed nodes in traversed, your getValue(int n) function can actually index into traversed to get the value you want. In your getValue(int n) function, just use code like this:
if (n < traversed.size()) {
return traversed.get(n).getValue();
}
throw new Exception("Element not existed");
To be able to use traversed, just return it in your queueOfTreaversed function.

How do I implement a priority queue with explicit links (using a triply linked datastructure)?

I am trying to implement a priority queue, using a triply linked data structure. I want to understand how to implement sink and swim operations, because when you use an array, you can just compute the index of that array and that's it. Which doesn't make sense when you use a triply-linked DS.
Also I want to understand how to correctly insert something in the right place, because when you use an array, you can just insert in the end and do a swim operation, which puts everything in the right place, how exactly do I compute that "end" in a linked DS?
Another problem would be removing the element with the biggest priority. To do that, for an array implementation, we just swap the last element with the first (the root) one and then, after removing the last element, we sink down the first one.
(This is a task from Sedgewick).
I posted this in case someone gets stuck doing this exercise from Sedgewick, because he doesn’t provide a solution for it.
I have written an implementation for maximum oriented priority queue, which can be modified according for any priority.
What I do is assign a size to each subtree of the binary tree, which can be defined recursively as size(x.left) + size(x.right) + 1. I do this do be able to find the last node inserted, to be able to insert and delete maximum in the right order.
How sink() works:
Same as in the implementation with an array. We just compare x.left with x.right and see which one is bigger and swap the data in x and max(x.left, x.right), moving down until we bump into a node, whose data is <= x.data or a node that doesn’t have any children.
How swim() works:
Here I just go up by doing x = x.parent, and swapping the data in x and x.parent, until x.parent == null, or x.data <= x.parent.
How max() works:
It just returns root.data.
How delMax() works:
I keep the last inserted node in a separate field, called lastInserted. So, I first swap root.data with lastInserted.data. Then I remove lastInserted by unhooking a reference to it, from its parent. Then I reset the lastInserted field to a node that was inserted before. Also we must not forget to decrease the size of every node on the path from root to the deleted node by 1. Then I sink the root data down.
How insert() works:
I make a new root, if the priority queue is empty. If it’s not empty, I check the sizes of x.left and x.right, if x.left is bigger in size than x.right, I recursively call insert for x.right, else I recursively call insert for x.left. When a null node is reached I return new Node(data, 1). After all the recursive calls are done, I increase the size of all the nodes on the path from root to the newly inserted node.
Here are the pictures for insert():
And here's my java code:
public class LinkedPQ<Key extends Comparable<Key>>{
private class Node{
int N;
Key data;
Node parent, left, right;
public Node(Key data, int N){
this.data = data; this.N = N;
}
}
// fields
private Node root;
private Node lastInserted;
//helper methods
private int size(Node x){
if(x == null) return 0;
return x.N;
}
private void swim(Node x){
if(x == null) return;
if(x.parent == null) return; // we're at root
int cmp = x.data.compareTo(x.parent.data);
if(cmp > 0){
swapNodeData(x, x.parent);
swim(x.parent);
}
}
private void sink(Node x){
if(x == null) return;
Node swapNode;
if(x.left == null && x.right == null){
return;
}
else if(x.left == null){
swapNode = x.right;
int cmp = x.data.compareTo(swapNode.data);
if(cmp < 0)
swapNodeData(swapNode, x);
} else if(x.right == null){
swapNode = x.left;
int cmp = x.data.compareTo(swapNode.data);
if(cmp < 0)
swapNodeData(swapNode, x);
} else{
int cmp = x.left.data.compareTo(x.right.data);
if(cmp >= 0){
swapNode = x.left;
} else{
swapNode = x.right;
}
int cmpParChild = x.data.compareTo(swapNode.data);
if(cmpParChild < 0) {
swapNodeData(swapNode, x);
sink(swapNode);
}
}
}
private void swapNodeData(Node x, Node y){
Key temp = x.data;
x.data = y.data;
y.data = temp;
}
private Node insert(Node x, Key data){
if(x == null){
lastInserted = new Node(data, 1);
return lastInserted;
}
// compare left and right sizes see where to go
int leftSize = size(x.left);
int rightSize = size(x.right);
if(leftSize <= rightSize){
// go to left
Node inserted = insert(x.left, data);
x.left = inserted;
inserted.parent = x;
} else{
// go to right
Node inserted = insert(x.right, data);
x.right = inserted;
inserted.parent = x;
}
x.N = size(x.left) + size(x.right) + 1;
return x;
}
private Node resetLastInserted(Node x){
if(x == null) return null;
if(x.left == null && x.right == null) return x;
if(size(x.right) < size(x.left))return resetLastInserted(x.left);
else return resetLastInserted(x.right);
}
// public methods
public void insert(Key data){
root = insert(root, data);
swim(lastInserted);
}
public Key max(){
if(root == null) return null;
return root.data;
}
public Key delMax(){
if(size() == 1){
Key ret = root.data;
root = null;
return ret;
}
swapNodeData(root, lastInserted);
Node lastInsParent = lastInserted.parent;
Key lastInsData = lastInserted.data;
if(lastInserted == lastInsParent.left){
lastInsParent.left = null;
} else{
lastInsParent.right = null;
}
Node traverser = lastInserted;
while(traverser != null){
traverser.N--;
traverser = traverser.parent;
}
lastInserted = resetLastInserted(root);
sink(root);
return lastInsData;
}
public int size(){
return size(root);
}
public boolean isEmpty(){
return size() == 0;
}
}

Find distance between two nodes in binary tree

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.

Java - The maximum sum of a path through a binary tree

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));
}

Categories