Counting matching nodes on a tree - java

I am trying this problem on Practice-It, but have been having trouble with it for quite a while.
Write a method matches that returns a count of the number of nodes in one tree that match nodes in another tree. A match is defined as a pair of nodes that are in the same position in the two trees relative to their overall root and that store the same data.
So far, I've tried the following below, but I don't quite get the count I want, and I'm not quite sure why.
public int matches(IntTree t2)
{
return match(overallRoot, t2.overallRoot);
}
public int match(IntTreeNode tree1, IntTreeNode tree2)
{
if(tree1 == null && tree2 == null)
return 1;
if(tree1 == null || tree2 == null)
return 0;
if(tree1.data == tree2.data)
return 1;
int left = match(tree1.left, tree2.left);
int right = match(tree1.right, tree2.right);
return left + right;
}
Any help would really be appreciated!

You're stopping your search if the current node matches. If it's different, you check left and right, but on a match you return one.

You are very close to the solution, you have to consider:
if one of the nodes is null you can stop the visit for the subtrees and return 0
if the data for the two roots are different the count is 0 otherwise is 1 and after you can calculate the count for the two subtrees adding to the count for the two roots.
Below my suggestions as code:
public int match(IntTreeNode tree1, IntTreeNode tree2) {
if(tree1 == null || tree2 == null) { return 0; }
int count = tree1.data == tree2.data ? 1 : 0;
int left = match(tree1.left, tree2.left);
int right = match(tree1.right, tree2.right);
return count + left + right;
}

Full answer for the practice it one:
int matches(IntTree tree2) {
return matches(tree2.overallRoot, this.overallRoot);
}
int matches(IntTreeNode tree1, IntTreeNode node2)
{
int left=0, right=0, count =0;
if(tree1 == null && this != null || this == null && tree1 != null) { return 0; }
count = tree1.data == node2.data ? 1 : 0;
if(tree1.left != null && node2.left !=null){
left = matches(tree1.left, node2.left);}
if(tree1.right != null && node2.right !=null){
right = matches(tree1.right, node2.right);}
return count + left + right;
}

Related

Why does my code not work? It passed 8 out of 600 cases on GFG. The Question is "leaf at same level"

My Approach is this- I used an arraylist. I checked all the nodes of the Binary Tree, to find those whose left and right node are null, which signifies that they are a leaf, then I found out their levels and added them to an arraylist.
After this I used the Arraylist in the function boolean check to check whether all the elements of the array list are same of not, if they are i return true (all leaves are at the same level) otherwise I return false.
class Solution {
boolean check(Node root) {
int c = 0;
ArrayList<Integer> a = new ArrayList<>();
for (int x : a) {
if (x != (a.get(0)))
return false;
}
return true;
}
public void che(Node root, int level, ArrayList<Integer> a) {
if (root == null) return;
if (root.left == null && root.right == null) {
a.add(level);
}
che(root.right, level + 1, a);
che(root.left, level + 1, a);
}
}
This is the link for the Question
The che function is never called.
It is however not necessary to collect data in an array list. Instead make the recursive function return the height of the subtree it is called on. In the same function compare the height that is returned for the left and right subtree. If they are different, return a special value to indicate failure (like -2), otherwise return that common height plus one.
This allows the function to abort the search as soon as a height difference is found, avoiding the unnecessary traversal of the rest of the tree.
Here is how that would look:
class Solution
{
boolean check(Node root) {
return height(root) > -2;
}
private int height(Node root) {
if (root == null) return -1;
int left = height(root.left);
if (left == -2) return -2;
int right = height(root.right);
if (left == -1 || right == -1 || left == right) {
return 1 + Math.max(left, right);
}
return -2;
}
}

Find if 2 trees have similar leaves(from left to right)?

As far as my logic goes I'm using two different arrays to store all the leafs and then compare those arrays to see if the leaves are indeed the same, but my test cases are failing (for eg. [3,5,1,6,2,9,8,null,null,7,4]
[3,5,1,6,7,4,2,null,null,null,null,null,null,9,8]). Thanks in advance!
'''
class Solution {
static int array1[] = new int[50];
static int array2[] = new int[50];
static int q = 0;
static int r = 0;
public boolean compareLeaves(int arr1[], int arr2[])
{
for(int i = 0; i <array1.length ;i ++)
{
if(array1[i] != array2[i])
{
return false;
}
}
return true;
}
public boolean leafSimilar(TreeNode root1, TreeNode root2) {
if(root1 == null || root2 == null)
{
return false;
}
if(root1.left == null && root1.right == null)
{
array1[q] =root1.val ;
q++;
}
if(root2.left == null && root2.right == null)
{
array2[r] =root2.val ;
r++;
}
leafSimilar(root1.left,root2.left);
leafSimilar(root1.right,root2.right);
return compareLeaves(array1,array2);
}
}
'''
If the arrays have different lengths but agree on the first array1.length elements, I believe you deem them equal (return true). You probably need to use q and r for determining whether the element counts are the same and how many elements to compare.
If both roots are null, I would expect that the trees should be considered equal, but you return false.
Even if root1 == null, you should still pick up the leaves from root2 and vice versa.
I think you should do in-order traversal, that is, call leafSimilar(root1.left,root2.left) before you look at root1.val and root2.val. It may be that it doesn’t matter since the val is only considered for leaves, but I find it hard to be 100 % sure.
I may have missed something.
Using two different arrays to store all the leaves should be a sound strategy. I think it will be easier if you process each tree separately, not both trees at once.
your program will fail for test case:
tree1 : 1
/ \
null null
tree2: 2
/ \
1 null
clearly both trees have only one leaf node 1 but your code will fail because you are iterating on both of them identically.
you should iterate on them separately and store leaf node in the array's. And in the end check if both array have same elements no matter the order.
tree1 : 1
/ \
2 3
tree2: 1
/ \
3 2
Above two trees have same leaves, I have Updated functions to implement it correctly.
public int leafSimilar(TreeNode root, int arr[], int l) {
if(root == null)
{
return l;
}
if(root.left == null && root.right == null)
{
arr[l] =root.val ;
l+=1;
return l;
}
l = leafSimilar(root.left, l);
l = leafSimilar(root.right, l);
return l;
}
public boolean compareLeaves(int arr1[], int arr2[], int l, int r)
{
if( l != r ) return false;
for(int i = 0; i <l ;i ++)
{
boolean flag = true;
for(int j = 0; j <r ;j ++) {
if(arr1[i] == arr2[j])
{
flag = false;
break;
}
}
if( flag) return false;
}
return true;
}
int l = leafSimilar(root1, arr1, 0);
int r = leafSimilar(root2, arr2, 0);
compareLeaves(arr1, arr2, l, r);
Also above function will fail if tree can have duplicate node. Update the compare function to count frequency of all nodes in array one and then match it with frequency of nodes in array2. It will handle duplicate nodes.
The below line of code is suggesting both the tree to follow the same path thereby, ignoring the leaves in one of the tree1 or tree2.
if(root1 == null || root2 == null)
{
return false;
}
It's better to traverse both the tree one by one. And keep on storing the leaves.
public static boolean compare()
{
for(int i = 0; i <array1.length ;i ++)
{
if(array1[i] != array2[i])
{
return false;
}
}
return true;
}
public void isSimilar(Node root, int flag)
{
if(root==null)
return;
if(root.left == null && root.right == null)
{
if(flag==1)
{
array1[q] =root.val ;
q++;
}
else
{
array2[r] =root.val ;
r++;
}
}
isSimilar(root.left,flag);
isSimilar(root.right,flag);
}
You have to pass a flag variable to point which array to populate.
For ex, here 0 refer to tree1 and populate array1, and 1 refer to tree2 and populate array2 :
isSimilar(root1, 0);
isSimilar(root2, 1);

Adding elements to tree

I have a binary tree and I'm trying to add elements
the problem is if I have and input like 5 5 6 6 9 0 0 2 -1 in the tree are added only 5 6 9 0 2 (I use -1 to stop adding elements)
I tried changing the condition if(nodeToAdd.data < node.data) in if(nodeToAdd.data <= node.data) but this didn't helped, I tried different conditions, but nothing, every time I got a stack overflow.
What I need to change so I could add two, three or n-times the same number? Any ideas?
Solved the problem, now everything work perfectly)
private Node traverseAndAddNode (Node node, int data){
if(node==null)return new Node(data);
if(data <= node.data){
node.leftChild=traverseAndAddNode(node.leftChild, data);
}else if(data > node.data){
node.rightChild=traverseAndAddNode(node.rightChild, data);
}
return node;
}
I just tried your code on my tree. Looks like it works.
private Node put(Node i, Key key, Value value) {
if (i == null) return new Node(key, value, 1);
// int compare = key.compareTo(i.key);
// if (compare < 0) i.left = put(i.left, key, value);
// else if(compare > 0) i.right = put(i.right, key, value);
// else i.value = value;
//
// //update N while unwinding stack
// i.N = size(i.left) + size(i.right) + 1;
int compare = key.compareTo(i.key);
if (compare < 0) {
if (i.left == null) i.left = new Node(key, value, 1);
else put(i.left, key, value);
}
if (compare >= 0) {
if (i.right == null) i.right = new Node(key, value, 1);
else put(i.right, key, value);
}
return i;
}
//main
BST<String, Integer> bst = new BST<>();
String[] s = {"S", "S", "E", "E", "X", "A", "R", "C", "H", "M"};
for (int i = 0; i < s.length; i++) {
bst.put(s[i], i);
}
for(String key: bst.keys()) System.out.print(key + " "); // levelordered
ouput: S E S A E X C R H M
Currently your method does nothing if nodeToAdd.data == node.data because both if conditions fail. You likely want:
if (nodeToAdd.data < node.data) {
...
else {
...
}
In this case equal nodes will be inserted to the right. If you want them inserted to the left then make the if condition <=.
None of that explains the stack overflow you mentioned but that is possibly an unrelated bug. I note that your code relies on the node being added having null children. I suggest that you explicitly assert this to ensure no loops are added to your tree in this method. They could well be added elsewhere.
assert nodeToAdd.leftChild == null;
assert nodeToAdd.rightChild == null;

Find numbers of nodes between root and give node

So in a given binary tree (node binary search tree), initially I want to find the number of nodes between two nodes "p" and "q". I first find the lowest common ancestor between these two nodes, say, "ancestor". Then I calculate the number of nodes between "ancestor" and "p" and number of nodes between "ancestor" and "q" separately and add them at last.
I tried recursive way to get number of nodes between "ancestor" and "p" or "q" but failed. Not a fan of recursive.
public static int NodeToNodePath(BinaryTree root, BinaryTree node, int length){
if(root == null && node == null)
return 0;
if(root == null || node == null)
return 0;
if(root.rootElement == node.rootElement){
return length;
}
int sum = NodeToNodePath(root.left, node, length + 1);
if(sum != 0)
return sum;
sum = NodeToNodePath(root.right, node, sum);
return sum;
}
But in this way, the result from root to left mode is correct but can't find node on the other size.
Any help?
Thanks!
I figure out how to solve the problem. By recursion. This may not apply to my origin problem
"find node number between two given nodes" but it does return number of nodes between root and a given node.
Code is posted below.
public static int NodeToNodePath(BinaryTree root,
BinaryTree node, int length) {
if(root == null)
return 0;
if(root.rootElement == node.rootElement){
length += 1;
return length;
}
int left = NodeToNodePath(root.left, node, length);
if(left != 0){
return left + 1;
}
int right = NodeToNodePath(root.right, node, length);
if(right != 0){
return right + 1;
}
return 0;
}
Also, I found a post on GeekForGeeks talking specifically about my origin problem, it's called "Find distance between two given keys of a Binary Tree" the address is:
http://www.geeksforgeeks.org/find-distance-two-given-nodes/
Thanks everyone!

Binary Search Tree Iterative Ceiling Method in Java

I'm working on a problem for class I'm stuck on. It involves adding a methods to the Binary Search Tree found here: http://algs4.cs.princeton.edu/32bst/BST.java.html
I need to develop an iterative ceiling method that finds the ceiling for a given key. It cannot be recursive.
Here is my code so far. I understand the basics of the algorithm I am supposed to implement, but I'm finding actually doing so hard to wrap my head around.
Thanks in advance for any help you might be able to offer.
public Key ceiling_i(Key key)
{
Node t = root;
for(int i = 0; i < size(); i++){
int cmp = key.compareTo(t.key);
if(cmp == 0) return t.key;
else if(cmp < 0) t = t.left;
else if(cmp > 0) t = t.right;
}
return null;
}
Edit: The main problem I am having is how to deal with the iterations after the first one. According to my book, "If a given key is greater than the key at the root of a BST,
then the ceiling of key (the largest key in the BST greater
than or equal to key) must be in the right subtree. If key is
less than the key at the root, then the ceiling of key could
be in the left subtree; if not (or if key is equal to the key
at the root), then the key at the root is the ceiling of key." I am not sure how to deal with that in the loop.
Your code is a good start. But your for loop does not make sense to me.
public Key ceiling_i(Key key)
{
Node t = root;
Node t largestVisited = null;
while(t != null) {
int cmp = key.compareTo(t.key);
if(cmp == 0) return t.key;
else if(cmp < 0) { largestVisited = Min(t, largestVisited); t = t.left; }
else if(cmp > 0) { t = t.right; largestVisited = Min(t, largestVisited); }
}
return largestVisited;
}
Node Min(Node a, Node b) { return the node with the smaller key; }
Tip: You could have derived this code by first writing the recursive solution and noticing that it is tail recursive. Tail recursive functions can trivially made non-recursive by just reusing the already existing local variables. No need to open another stack-frame if you won't ever use the old one again.
The code from that book is NOT tail-recursive because the first ceiling() call has operations done on it before the returns.
private Node ceiling(Node x, Key key) {
if (x == null) return null;
int cmp = key.compareTo(x.key);
if (cmp == 0) return x;
if (cmp < 0) {
Node t = ceiling(x.left, key);
if (t != null) return t;
else return x;
}
return ceiling(x.right, key);
}
Change ceilng() so the recursive calls are an "accumulator" type of tail-call. Do this by passing additional parameters which hold the work done so far.
Null or a node is passed as x.left when cmp < 0. What has "accumulated" so far is a larger-than-item node x found at this conditional test.
In the original version after the the first recursive call, t is either null or some tree node. Node x is then used after if t is null. In a modified version an additional parameter will be passed the x node.
In the other conditional test of cmp > 0 with the tail call, there is no new "accumulated" work since x is smaller than the item and is not used in deciding a return value if x.right is null.
Notice what happens if we pass an "accumulated x" to "larger" in a modified ceiling(x, key, larger) function. In this modified function, the condition "if (x == null) return null" is replaced by "if (x == null) return larger" and all the t-value evaluations are removed after the first recursive call. With the second tail-call just pass null to larger.
So the conversion should look something like this:
private Node ceiling (Node x, Key key, Node larger) {
if (x == null) return larger;
int cmp = key.compareTo(x.key);
if (cmp == 0) return x;
if (cmp < 0) {
return ceiling(x.left, key, x);
}
return ceiling(x.right, key, null);
}
Now the function is tail-recursive and can be further converted into an iterative loop form.
private Node ceiling (Node x, Key key) {
Node larger = null;
while (x != null) {
int cmp = key.compareTo(x.key);
if (cmp == 0) return x;
if (cmp < 0) {
larger = x;
x = x.left;
} else {
x = x.right;
}
}
return larger;
}

Categories