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

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

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

Counting matching nodes on a tree

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

how to call a recursion call N number of times, given the number N?

I have an array of numbers: S= {4,5} and I want to check if this group creates the sum = 13.
In this case, yes: 4 + 4 + 5 = 13
Another example: s={4,5}, sum = 6 -> no
I wrote a recursive function to solve this:
public static boolean isSumOf(int [] s,int n)
{
if(n == 0)
return true;
if(n < 0)
return false;
return isSumOf(s,n-s[0]) || isSumOf(s,n-s[1]);
}
But this function works only for 2 numbers in the array.
I need to write a recursive function that will deal with N numbers, like {4,9,3} or {3,2,1,7} etc.
I'm not sure how can I do this? How can I call a recursion N times, according to the length of the array? Or maybe I should change my algorithm completely?
Also - I'm not allowed to use loops.
return isSumOf(s,n-s[0]) || isSumOf(s,n-s[1]);
You can generalize this with a loop like this:
for (int i = 0; i < s.length; ++i) {
if (isSumOf(s,n-s[i])) return true;
}
return false;
But, since you can't use loops, you can write the equivalent loop as another recursive method:
boolean withoutLoop(int [] s,int n, int i) {
if (i >= s.length) return false;
return isSumOf(s,n-s[i]) || recurse(s, n, i+1);
}
and then call it like so from your isSumOf method:
public static boolean isSumOf(int [] s,int n)
{
if(n == 0)
return true;
if(n < 0)
return false;
return withoutLoop(s, n, 0); // Change here.
}
Or, if you want to write it more concisely:
return (n == 0) || (n < 0 && withoutLoop(s, n, 0));
Break the problem down:
Sum the numbers
Is the sum equal to 13?
Then think of a way to express summing an array as recursive task; e.g. Sum of elements 1 to N is element 1 + Sum of elements 2 to N.
Finally, turn that idea / expression into code.
For any recursion problem, use the template:
ResultType recursiveMethod(params) {
if( /* this is the simplest case */ ) {
return answer for the simplest case
} else {
partialResult = solve part of the problem
resultForRest = recursiveMethod(rest of problem)
}
}
Particularly for list processing, this becomes:
if(list is empty) {
return solution for an empty list
} else {
r = call self recursively for tail of list
return solution for head of list combined with r
}
(Where "head" is the first item, and "tail" is the rest. Tail may be empty.)
For your problem, the simplest case is an empty array:
if(s.length == 0) {
return n == 0;
}
For the else, the "part of the problem" is s[0] and the "rest of the problem" is s[1] onwards.
...
} else {
int head = s[0];
int[] tail = Arrays.copyOfRange(s,1,s.length-1);
return isSumOf(tail, n - head);
}
The code would be cleaner (and probably more efficient) if you used a List instead of an array directly, because you could then use List.subList() instead of copyOfRange().
You could also pass the whole array each time, along with an extra parameter indicating how much of the array has already been accounted for.
This should work:
public static boolean isSumOf(int [] s,int n)
{
if(n == 0)
return true;
if(n < 0)
return false;
for (int x: s) {
if (isSumOf(s, n-x)) {
return true;
}
}
return false;
}
UPDATE:
Oh! no loop, only recursion, you will need an extra argument:
public static boolean isSumOf(int [] s,int n)
{
if(n == 0)
return true;
if(n < 0)
return false;
return isSum2(s, n, 0);
}
public static boolean isSum2(int [] s,int n,int i)
{
if (i >= s.length)
return false;
return isSumOf(s,n-s[i]) || isSum2(s,n,i+1);
}

Can't figure out how to make method recursive

For my programming course I have to write recursive functions, but aside from the theoretical questions given during the classes I can't figure out how to do it with my own code.
If anyone could help me out and give me a pointer on where to start it'd be great!
The method is as follows:
public boolean hasColumn(Marble m) {
boolean hasColumn = false;
for (int i = 0; i < DIM && hasColumn == false; i++) {
int winCount = 0;
for (int j = 0; j < DIM && hasColumn == false; j++) {
if (j == 0) {
winCount = 1;
} else {
if (getField(j, i).equals(getField(j - 1, i))
&& getField(j, i).equals(m)) {
winCount++;
if (winCount == WINLENGTH) {
hasColumn = true;
}
} else {
winCount = 1;
}
}
if (!(getField(j, i).equals(m))) {
hasColumn = false;
}
}
}
return hasColumn;
}
There's a field[DIM][DIM], which stores Marbles. Marble has a Mark, which is 0-4, with 0 being empty and 1-4 being colour values. The method determines whether someone has a marble column of 5 and wins.
Input is the Marble type of a player. Output is boolean hasColumn true or false. The output value is correct, there's just no recursion.
The idea is to make it find a vertical column in a recursive way. This also has to be done with horizontal/vertical, but I figured when I get this figured out I'll manage those by myself.
Thank you in advance!
public boolean hasColumn(Marble m, int i, int j, int wincount) {
if (wincount == WINLENGTH)
return true;
if (i == DIM)
return false;
if (j == DIM)
return hasColumn(m, i + 1, 0, 0);
return hasColumn(m, i, j + 1, getField(j, i).equals(m) ? wincount + 1 : 0);
}
Depending on whether you'd like to find a line/column of elements equal to a given Marble element or rather of same value, you may call this method:
hasColumn(aMarble, 0, 0, 0);
hasColumn(getField(0, 0), 0, 0, 0);
There's a duality between certain types of recursion and iteration.
Consider that in your iterative function you are iteratinng over columns using two variables, i and j. Could you transform those local variables into parameters to the function? You would be transforming state internal to the function (local variables) into state implicit in the function call.
Looks like task sounds like:
1. We have a square matrix of Marble elements(it can be simple integers) with dimension DIM.
2. We have a method getField(int, int) return a marble from this matrix
3. We have an iterative decision to discover if this matrix has any column with equal values of marble elements
Our goal is write recursive variant of this method
So, look here. Recursive algorithm check ROW existing with same value:
public class Marble {
public static final int DIM = 10;
public int[][] marbleAr = new int[DIM][DIM];
public void init(){
for(int i=0;i<DIM;i++){
for(int j=0;j<DIM;j++){
marbleAr[i][j] = new Random().nextInt(10);
if(i == 2){
marbleAr[i][j] = 7;
}
}
}
}
public int get(int i, int j){
return marbleAr[i][j];
}
public void printMarbleAr(){
for(int i=0;i<DIM;i++){
for(int j=0;j<DIM;j++){
System.out.print(marbleAr[i][j] + " ");
}
System.out.println();
}
}
public boolean hasColumn(int val, int col, int row){
if(row == 0){
return true;
}
if(this.hasColumn(val, col, row-1)){
if(this.get(col, row) == this.get(col,row-1)){
return true;
}else{
if(col == DIM-1){
return false;
}
return this.hasColumn(val, col+1, row);
}
}
return false;
}
public static void main(String[] args) {
int v = 7;
Marble marble = new Marble();
marble.init();
marble.printMarbleAr();
System.out.println(marble.hasColumn(v, 0, DIM-1));
}
}
Your method name is hasColumn and return variable
name is hasColumn. That's BAD.
I don't see hasColumn invoked inside the method again to actually
go down to recursion path.

Dealing with Union-Find algorithms with a lot of objects

I have a problem with (not anymore with stackoverflow (hehe)) Find algorithm when trying to implement UnionFind structure algorithm with path-compression.
I have standard array of ints, array can get pretty big -> it works fine until 60.000.000 elements.
My Union function looks like this:
public void unite(int p, int q) {
if(p >= 0 && p < id.length && q >= 0 && q < id.length){
if (isInSameSet(p, q)) return;
id[find(p)] = find(q);
stevilo--;
}
}
My isInSameSet looks like this:
public boolean isInSameSet(int p, int q) {
if(p >= 0 && p < id.length && q >= 0 && q < id.length)
return find(p) == find(q);
return false;
}
I have tried iterative way in Find:
public int find(int i) {
while (i != id[i]){
id[i] = id[id[i]];
i = id[i];
}
return i;
}
and tail-recrusion:
public int find(int i) {
int p = id[i];
if (i == p) {
return i;
}
return id[i] = find(p);
}
Is there anything I missed in my code? Is there any other approach to this kind of problems?
#edit: Adding constructor to code:
public UnionFind(int N) {
stevilo = N;
id = new int[N];
for(int i = 0; i < N; i++){
id[i] = i;
}
#edit2 (better explanation and new findings):
The problem is not in stackoverflow anymore for less then 60.000.000 elements, which is more then enough for solving my problems.
I'm calling test Unions like this:
for(i=0;i<id.length-1;i++)
unite(i,i+1)
so the ending pairs are like this:
0:1, 1:2, 2:3, 3:4,..
which is only example of least optimal option for testing means only :)
Then I check if representative of 0 is last element in table (99 for 100 elements) and it works.
Problem is, that my algorithm works only if initial elements are each in their own union (0:0, 1:1, 2:2, 3:3). If I have different Unions already set up (0:2, 1:6, 2:1, 3:5, ...) my testing algorithm stops working.
I have narrow it down to a problem in Find function, probably something to do with path compression
id[i] = id[id[i]].
One small optimization would be to get rid of isInSameSet...
public void unite(int p, int q) {
if(p >= 0 && p < id.length && q >= 0 && q < id.length){
int rootp = find(p);
int rootq = find(q);
if (rootp==rootq) return;
id[rootp] = rootq;
stevilo--;
}
}
Union-Find data structures typically include TWO different optimizations. One is path compression. You have that.
But the other optimization happens during a Union, where you carefully choose which of the two roots to make a child of the other, usually via Union-By-Rank or Union-By-Size. With that optimization, your trees should never be deep enough to get a stack overflow. However, that optimization seems to be missing from your unite function.
I once wrote an algorithm for UnionFind, and its time complexity is O(log*(n)). Thats iterative logarithm of n. The algorithm compresses the path of the tree as it keeps on connecting the nodes to gain efficiency. I find it very efficient, though I haven't practically tested it against huge array size. Here's the code:
public class UnionFind
{
private int[] id;
public UnionFind(int capacity)
{
id = new int[capacity];
for (int i = 0; i < capacity; i++)
{
id[i] = i;
}
}
public boolean isConnected(int p, int q)
{
return root(p) == root(q);
}
public void connect(int p, int q)
{
if (isConnected(p, q))
{
return;
}
id[root(p)] = root(q);
}
private int root(int p)
{
int temp = p;
if (p != id[p] && id[id[p]] != id[p])
{
while (p != id[p])
{
p = id[p];
}
id[temp] = id[p];
}
return id[p];
}
}

Categories