0

Okay, so I've got a basic Binary Search tree class I've made, which depends on an internal Node class to store the data. It goes something like this (just showing the bare bones here):

public class TreeSet<T> where T : IComparable<T>
{
    private Node root = null;

    public void Insert(T t)
    {
        if (root == null)
            root = new Node(t);
        else
            root.Insert(t);
    }

    internal class Node
    {
        protected Node left, right;
        private T data;

        public Node(T t)
        {
            data = t;
        }

        public virtual void Insert(T t)
        {
            int compare = t.CompareTo(data);
            if (compare < 0)
            {
                if (left == null)
                    left = new Node(t);
                else
                    left.Insert(t);
            }
            else if (compare > 0)
            {
                if (right == null)
                    right = new Node(t);
                else
                    right.Insert(t);
            }
        }
    }
}

There's more to it, obviously search methods, enumerators, etc., but these are the relevant bits. Now this is just a basic BST, and doesn't do anything to try and ensure it's balanced or anything, but it handles all the insertion and searching logic. If I were to try and extend this to make an AVL tree or Red-Black or something, we run into problems...

public class AvlTree<T> : TreeSet<T> where T : IComparable<T>
{
    internal new class Node : TreeSet<T>.Node
    {
        public Node(T t) : base(t) {}

        public int Depth
        {
            get
            {
                if (left == null && right == null)
                    return 0;
                else if (left == null)
                    return right.Depth + 1;
                else if (right == null)
                    return left.Depth + 1;
                else
                    return Max(left.Depth, right.Depth) + 1;
            }
        }

        public override void Insert(T t)
        {
            base.Insert(t);

            //Do the AVL balancing stuff
        }
    }
}

Already there's a problem - the left and right variables are declared in the parent class to be TreeSet<T>.Node, and are not AvlTree<T>.Node. But I need the Depth variable to determine if balancing is needed. I can cast them, of course, but that won't work because the nodes are created in the parent's Insert() function, and will not be the right class. I could of course just copy all the code into a new, non-extended class, but that's a lot of duplicated code and most of it will be the same. (I might also make a Red-Black or Splay tree, which would also be mostly the same except for the balancing algorithms.) And ultimately, it can be useful to have one parent class for all such BSTs, so I could change which tree type I'm using without altering the code using the tree. (Until this goes full scale, it's hard to say which balancing algorithm will perform better.)

Is there any good way to handle this sort of thing? That is - making sure the left and right variables store the appropriate sub-class, and are created as the correct class, even if they are created by the parent class? Or is it just a mistake to even try to reuse the code in this case, and I'd be better off making the AvlTree completely separate from the TreeSet, despite the duplicated code?

Darrel Hoffman
  • 4,436
  • 6
  • 29
  • 41
  • The first thing I'd say is don't do all of your real work in the `Node` class. Have it just be a data holder that has data and two node references. Have your main class traverse the node properties given that it will have access to them. It can make certain operations a lot easier when you do that. It lets you 1) avoid recursion when you want to and 2) make it generally easier to access data "up the stack" i.e. the parent node, or some previous node you were working on in the operation. It makes it a lot easier in particular when dealing with a red/black tree. – Servy Jun 27 '13 at 20:11
  • @Servy The problem with that (with an AVL tree at least) is that without recursion, you can't easily go back up the inserted node's parentage to check for imbalances - unless you add the overhead of a parent link to every node, or store a list of all visited nodes while inserting to go back and check, or start back from the root and go down to the inserted node again, which basically doubles the time it takes. Might need the parent for red-black (maybe? I haven't tried one before), but it can be avoided with AVL. – Darrel Hoffman Jun 27 '13 at 21:01
  • Which is why I proposed *not* using recursion, and moving the scope of your logic to a location where a non-recursive solution makes more sense... – Servy Jun 27 '13 at 21:02
  • But all of the non-recursive solutions add extra memory overhead (parent links) or time overhead (repeat search from top after insertion) for AVL. Anyhow, it still doesn't solve the polymorphism problem, which is that AVL nodes need depth, and red-black needs color, etc., while a basic BST node doesn't. How would the parent class know which to create without duplicating all that code in every sub-class? – Darrel Hoffman Jun 27 '13 at 21:19

0 Answers0