Google

Jan 9, 2014

Java Tree structure interview questions and coding questions -- Part 3

This is an extension to Java Tree structure interview questions and coding questions -- Part 2, and adds functional programming and recursion.


Step 1: The Tree interface with get( ) method that returns either a Triple tree or Leaf data.

package com.mycompany.flatten;

public interface Tree<T>
{
    abstract Either<T, Triple<Tree<T>>> get();
}

Step 2: The Leaf that implements the Tree interface.

package com.mycompany.flatten;

public class Leaf<T> implements Tree<T>
{
    
    private final T data;
    
    public static <T> Tree<T> leaf(T value)
    {
        return new Leaf<T>(value);
    }
    
    public Leaf(T t)
    {
        this.data = t;
    }
    
    public T getData()
    {
        return data;
    }
    
    @SuppressWarnings("unchecked")
    public Either<T, Triple<Tree<T>>> get()
    {
        return Either.left(data);
    }
    
    @Override
    public String toString()
    {
        return "Leaf [data=" + data + "]";
    }  
}

Step 3: The Node with Triple tree that implements the Tree interface.

package com.mycompany.flatten;

public class Node<T> implements Tree<T>
{
    
    private final Triple<Tree<T>> branches;
    
    public static <T> Tree<T> tree(T left, T middle, T right)
    {
        return new Node<T>(Leaf.leaf(left), Leaf.leaf(middle), Leaf.leaf(right));
    }
    
    public Node(Tree<T> left, Tree<T> middle, Tree<T> right)
    {
        this.branches = new Triple<Tree<T>>(left, middle, right);
    }
    
    public Either<T, Triple<Tree<T>>> get()
    {
        return Either.right(branches);
    }
    
    public Triple<Tree<T>> getBranches()
    {
        return branches;
    }
    
    @Override
    public String toString()
    {
        return "Node {branches=" + branches + "}";
    }
    
}

Step 4: The Triple class used by the Node.




package com.mycompany.flatten;

/**
 * A type that stores three values of the same type.
 */
public class Triple<T>
{
    
    private final T left, middle, right;
    
    public Triple(T l, T m, T r)
    {
        this.left = l;
        this.middle = m;
        this.right = r;
    }
    
    public T left()
    {
        return left;
    }
    
    public T middle()
    {
        return middle;
    }
    
    public T right()
    {
        return right;
    }
    
    @Override
    public String toString()
    {
        return "Triple [l=" + left + ", m=" + middle + ", r=" + right + "]";
    }
    
}

Step 5: As you can see that the Node and Leaf are using the class Either to handle Node and Leaf differently. The Either stores left or right values but not both. The Leaf uses the left and the Node uses the right. You can pass in a Function to be executed for the leaf and node.

package com.mycompany.flatten;

/**
 * X type which stores one of either of two types of value, but not both.
 */
public class Either<X, Y>
{
    private final X x;
    private final Y y;
    
    private Either(X x, Y y)
    {
        this.x = x;
        this.y = y;
    }
    
    /**
     * Constructs x left-type Either
     */
    public static <X> Either left(X x)
    {
        if (x == null)
            throw new IllegalArgumentException();
        return new Either(x, null);
    }
    
    /**
     * Constructs x right-type Either
     */
    public static <Y> Either right(Y y)
    {
        if (y == null)
            throw new IllegalArgumentException();
        return new Either(null, y);
    }
    
    /**
     * Applies function f to the contained value if it is x left-type and
     * returns the result.
     */
    public void ifLeft(Function<X> f)
    {
        if (!this.isLeft())
        {
            throw new IllegalStateException();
        }
        
        f.apply(x);
        
    }
    
    /**
     * Applies function f to the contained value if it is x right-type and
     * returns the result.
     */
    public void ifRight(Function<Y> f)
    {
        if (this.isLeft())
        {
            throw new IllegalStateException();
        }
        
        f.apply(y);
        
    }
    
    /**
     * @return true if this is x left, false if it is x right
     */
    public boolean isLeft()
    {
        return y == null;
    }
    
    @Override
    public String toString()
    {
        return "Either [x=" + x + ", y=" + y + "]";
    }
    
}

Step 6: Define the Function interface

package com.mycompany.flatten;

public interface Function<P>
{
    
    void apply(P p);
}

Step 7: Define two different implementations for the FunctionLeafPrint and NodePrint for printing Leaf and Node respectively.

package com.mycompany.flatten;

public class LeafPrint<P> implements Function<P>
{
    
    public void apply(P p)
    {
        System.out.println("--> Leaf:" + p);
    }
    
}


package com.mycompany.flatten;

public class NodePrint<P> implements Function<P>
{
    
    public void apply(P p)
    {
        Triple t = (Triple<P>) p;
        System.out.println("left ==>  " + t.left() + " || middle ==> " + t.middle() + " || right ==> " + t.right());
    }
    
}

Step 8: The FlattenTree interface that works on the Tree.

package com.mycompany.flatten;

public interface FlattenTree<T>
{
    void flatten(Tree<T> tree);
}

Step 9: Implementation of FlattenTree interface RecursiveFlattenTree.


package com.mycompany.flatten;

public class RecursiveFlattenTree<T> implements FlattenTree<T>
{
    
    public void flatten(Tree<T> tree)
    {
        if (tree == null)
        {
            return;
        }
        
        Either<T, Triple<Tree<T>>> either = tree.get();
        
        if (either.isLeft())
        {
            either.ifLeft(new LeafPrint<T>());
        }
        
        else
        {
            either.ifRight(new NodePrint<Triple<Tree<T>>>());
            Triple<Tree<T>> trippleTree = ((Node<T>) tree).getBranches();
            flatten(trippleTree.left());   // recursion
            flatten(trippleTree.middle()); // recursion
            flatten(trippleTree.right());  // recursion
        }
        
    }
}

Step 10: Finally, the SpecialTreeTest test class with main method.

package com.mycompany.flatten;

public class SpecialTreeTest
{
    
    public static void main(String[] args)
    {
        
        Tree<String> leafB21 = Leaf.leaf("B21");
        Tree<String> leafB22 = Leaf.leaf("B22");
        Tree<String> leafB23 = Leaf.leaf("B23");
        
        //takes all 3 args as "Leaf<String>" and returns "Tree<Leaf<String>>"
        Tree<Tree<String>> level3 = Node.tree(leafB21, leafB22, leafB23);
        
        Tree<String> leafB1 = Leaf.leaf("B1");
        Tree<String> leafB3 = Leaf.leaf("B3");
        
        //takes  3 args as "Leaf<String>", "Tree<Leaf<String>>", and  "Leaf<String>" 
        Tree<Tree<String>> level2 = new Node(leafB1, level3, leafB3);
        
        Tree<Tree<String>> level1 = new Node(Leaf.leaf("A"), level2, Leaf.leaf("C"));
        
        //System.out.println(level1); //level1 is the root
        
        FlattenTree<Tree<String>> flatTree = new RecursiveFlattenTree<Tree<String>>();
        flatTree.flatten(level1);
        
    }
    
}

The ouput

left ==>  Leaf [data=A] || middle ==> Node {branches=Triple [l=Leaf [data=B1], m=Node {branches=Triple [l=Leaf [data=Leaf [data=B21]], m=Leaf [data=Leaf [data=B22]], r=Leaf [data=Leaf [data=B23]]]}, r=Leaf [data=B3]]} || right ==> Leaf [data=C]
--> Leaf:A
left ==>  Leaf [data=B1] || middle ==> Node {branches=Triple [l=Leaf [data=Leaf [data=B21]], m=Leaf [data=Leaf [data=B22]], r=Leaf [data=Leaf [data=B23]]]} || right ==> Leaf [data=B3]
--> Leaf:B1
left ==>  Leaf [data=Leaf [data=B21]] || middle ==> Leaf [data=Leaf [data=B22]] || right ==> Leaf [data=Leaf [data=B23]]
--> Leaf:Leaf [data=B21]
--> Leaf:Leaf [data=B22]
--> Leaf:Leaf [data=B23]
--> Leaf:B3
--> Leaf:C


You may also like:

Labels: , ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home