Home > Software design >  Making a Binary search Tree Generic
Making a Binary search Tree Generic

Time:12-19

public double sum(TreeNode root){
    Queue<TreeNode> queue = new ArrayDeque<>();
    double sum = 0;
    if(root!=null){
        queue.add(root);
    }
    while(!queue.isEmpty()){
        int size = queue.size();
        for (int i = 0; i < size; i  ) {
            TreeNode current = queue.remove();
            sum  = current.value;
            if(current.leftChild!=null)
                queue.add(current.leftChild);
            if(current.rightChild!=null)
                queue.add(current.rightChild);
        }
    }
    return sum;
}

Now I need to implement a generic binary search Tree which can store in the value field of a node either a Character, an Integer or Double (if Character is a generic parameter of the Tree, then its ASCII code sum will be returned from the sum() method).

And I need to perform various operations, like sum() of all the node's values (also insert, delete, search, maximum, minimum, etc.).

But I'm stuck on making my implementation generic. In the sum() method issues an error in the line

sum  = current.value;

Operator cannot be applied to T.

How can I resolve it?

CodePudding user response:

Arithmetic operators can not be used with an arbitrary type, they are only applicable for numeric primitives and their wrapper types. So if you need to generify your logic, it not the way to go.

Instead, you can define a field of type BiFunction, which is a Function expecting two arguments, on the level of your Tree class (or whatever its name). And use this BinaryOperator to accumulate the values of the nodes while traversing the tree.

Based on this quote:

if Character is a generic parameter of the Tree, then its ASCII code sum will be returned from the sum() method

I assume that you need to keep the return type of the sum() method as double. Since according to the requirements of your assignment only three generic types: Character, Integer or Double need to be supported, that's doable.

For that the BiFunction mentioned above, can be defined as BiFunction<T,Double,Double>, where the first two generic parameters T and Double denote the types of incoming parameters of the function and the last type Double specifies the return type. Here are examples of implementation of such function:

BiFunction<Double, Double, Double> intMerger = Double::sum;
BiFunction<Integer, Double, Double> intMerger = (i, d) -> i   d;
BiFunction<Character, Double, Double> intMerger = (c, d) -> c   d;

Also, since you need to perform operations like binary search in your generic tree you need either define a generic parameter T as extending Comparable interface, or provide a Comparator (otherwise you'll not be able to determine which value is greater/smaller while implementing a binary search, or finding min/max value). Since all the types that should be supported are comparable, you can use the first option and define the Generic parameter as T extends Comparable<T>, which means in plain English: something that knows how to compare itself.

So as a result, you might have something like this:

public class MyTree<T extends Comparable<T>> {
    private final BiFunction<T, Double, Double> merger;

    public MyTree(BiFunction<T, Double, Double> merger) {
        this.merger = merger;
    }

    public double sum(TreeNode<T> root) {
        double total = 0;
        if (root == null) return total;
        
        Queue<TreeNode<T>> queue = new ArrayDeque<>();
        queue.add(root);
        
        while (!queue.isEmpty()) {
            int size = queue.size();
            TreeNode<T> current = queue.remove();

            total = merger.apply(current.value, total);
            
            if (current.leftChild != null) queue.add(current.leftChild);
            if (current.rightChild != null) queue.add(current.rightChild);
        }
        return total;
    }
    
    private static class TreeNode<T extends Comparable<T>> {
        private T value;
        private TreeNode<T> leftChild;
        private TreeNode<T> rightChild;
        
        // constructor, etc.
    }
}

Usage example:

MyTree<Double> myTree1 = new MyTree<>(Double::sum);
MyTree<Integer> myTree2 = new MyTree<>((i, d) -> i   d);
MyTree<Character> myTree3 = new MyTree<>((c, d) -> c   d);

Note: if what I was telling about introducing a Function looks alien to you have a look at this tutorial provided by Oracle.

  • Related