Trees

Tree ADT

  • In computer science, a tree is an abstract model of a hierarchical structure
  • A tree consists of nodes with a parent-child relation
  • Terminology
    • Root: node without parent (A)
    • Internal node: node with at least one child (A, B, C, F)
    • External node (a.k.a. leaf ): node without children (E, I, J, K, G, H, D)
    • Ancestors of a node: parent, grandparent, grand-grandparent, etc.
    • Depth of a node: number of ancestors
    • Height of a tree: maximum depth of any node (3)
    • Descendant of a node: child, grandchild, grand-grandchild, etc.
    • Subtree: tree consisting of a node and its descendants
  • Applications: Organization charts, Filesystems
  • We use positions to abstract nodes
  • Generic methods
    • integer size()
    • boolean isEmpty()
    • objectIterator elements()
    • positionIterator positions()
  • Accessor methods:
    • position root()
    • position parent(p)
    • positionIterator children(p)
  • Query methods:
    • boolean isInternal(p)
    • boolean isExternal(p)
    • boolean isRoot(p)
  • Update methods:
    • swapElements(p, q)
    • object replaceElement(p, o)
  • Additional update methods may be defined by data structures implementing the Tree ADT

Preorder Traversal

  • A traversal visits the nodes of a tree in a systematic manner
  • In a preorder traversal, a node is visited before its descendants
  • Application: print a structured document
Algorithm preOrder(v)
  visit(v)
  for each child w of v
    preOrder (w)

Postorder Traversal

  • In a postorder traversal, a node is visited after its descendants
  • Application: compute space used by files in a directory and its subdirectories
Algorithm postOrder(v)
  for each child w of v
    postOrder (w)
  visit(v)

Inorder Traversal

  • In an inorder traversal a node is visited after its left subtree and before its right subtree
  • Application: draw a binary tree
    • x(v) = inorder rank of v
    • y(v) = depth of v
Algorithm inOrder(v)
  if isInternal (v)
    inOrder (leftChild (v))
  visit(v)
  if isInternal (v)
    inOrder (rightChild (v))

Euler Tour Traversal

  • Generic traversal of a binary tree
  • Includes a special cases the preorder, postorder and inorder traversals
  • Walk around the tree and visit each node three times:
    • on the left (preorder)
    • from below (inorder)
    • on the right (postorder)
public abstract class EulerTour {
  protected BinaryTree tree;

  protected void visitExternal(Position p, Result r) { }
  protected void visitLeft(Position p, Result r) { }
  protected void visitBelow(Position p, Result r) { }
  protected void visitRight(Position p, Result r) { }
  protected Object eulerTour(Position p) {
    Result r = new Result();
    if tree.isExternal(p) {
      visitExternal(p, r);
    } else {
      visitLeft(p, r);
      r.leftResult = eulerTour(tree.leftChild(p));
      visitBelow(p, r);
      r.rightResult = eulerTour(tree.rightChild(p));
      visitRight(p, r);
      return r.finalResult;
    }
  }
}

public class EvaluateExpression extends EulerTour {
  protected void visitExternal(Position p, Result r) {
    r.finalResult = (Integer) p.element();
  }

  protected void visitRight(Position p, Result r) {
    Operator op = (Operator) p.element();
    r.finalResult = op.operation(
      (Integer) r.leftResult,
      (Integer) r.rightResult
    );
  }
}

Binary Tree

  • A binary tree is a tree with the following properties:
    • Each internal node has two children
    • The children of a node are an ordered pair
  • We call the children of an internal node left child and right child
  • Alternative recursive definition: a binary tree is either
    • a tree consisting of a single node, or
    • a tree whose root has an ordered pair of children, each of which is a binary tree
  • Applications:􀂄 arithmetic expressions, decision processes, searching
  • Properties
    • n number of nodes: n = 2e − 1
    • e number of external nodes: e = i + 1; e ≤ 2h
    • i number of internal nodes
    • h height: h ≤ i􀂄; h ≤ (n − 1)/2; h ≥ log2 e; h ≥ log2 (n + 1) − 1
  • BinaryTree ADT
    • The BinaryTree ADT extends the Tree ADT, i.e., it inherits all the methods of the Tree ADT
    • Additional methods: 􀂄position leftChild(p), position rightChild(p), position sibling(p)
    • Update methods may be defined by data structures implementing the BinaryTree ADT

Arithmetic Expression Tree

Binary tree associated with an arithmetic expression

  • internal nodes: operators
  • external nodes: decisions
  • Specialization of an inorder traversal
    • print operand or operator when visiting node
    • print “(“ before traversing left subtree
    • print “)“ after traversing right subtree
Algorithm printExpression(v)
  if isInternal (v)
    print(“(’’)
    inOrder (leftChild (v))
  print(v.element ())
  if isInternal (v)
    inOrder (rightChild (v))
    print (“)’’)

Evaluate Arithmetic Expressions

  • Specialization of a postorder traversal
    • recursive method returning the value of a subtree
    • when visiting an internal node, combine the values of the subtrees
Algorithm evalExpr(v)
  if isExternal (v)
    return v.element ()
  else
    x ← evalExpr(leftChild (v))
    y ← evalExpr(rightChild (v))
    ◊ ← operator stored at v
    return x ◊ y

Decision Tree

Binary tree associated with a decision process

  • internal nodes: questions with yes/no answer
  • external nodes: operands

Trees in JDSL

  • JDSL is the Library of Data Structures in Java
  • Tree interfaces in JDSL
    • InspectableBinaryTree
    • InspectableTree
    • BinaryTree
    • Tree
  • Inspectable versions of the interfaces do not have update methods
  • Tree classes in JDSL: NodeBinaryTree, NodeTree

Code

package examples;

import java.util.Iterator;


public class MyTree<E> implements Tree<E> {

    // auxiliary class for the tree nodes
    class TNode implements Position<E>{
        E elem;
        TNode parent;
        MyLinkedList<TNode> children = new MyLinkedList<>();
        Position<TNode> childrenPos; // Position in the children LinkedList
        Object creator = MyTree.this;

        @Override
        public E element() {
            return elem;
        }

    }

    // instance variables


    private TNode root;
    private int size;

    // instance methods  

    private TNode castToTNode(Position<E> p){
        TNode n;
        try {
            n = (TNode) p;
        } catch (ClassCastException e) {
            throw new RuntimeException("This is not a Position belonging to myTree");
        }
        if (n.creator == null) throw new RuntimeException("position was allready deleted!");
        if (n.creator != this) throw new RuntimeException("position belongs to another MyTree instance!");            
        return n;
    }


    @Override
    public Position<E> root() {
        return root;
    }

    @Override
    public Position<E> createRoot(E o) {
        root = new TNode();
        root.elem=o;
        return root;
    }

    @Override
    public Position<E> parent(Position<E> child) {
        TNode n = castToTNode(child);
        return n.parent;
    }

    @Override
    public Iterator<Position<E>> childrenPositions(Position<E> parent) {
        TNode n = castToTNode(parent);
        return new Iterator<Position<E>>() {
            Iterator<TNode> it = n.children.elements();
            @Override
            public boolean hasNext() {
                return it.hasNext();
            }

            @Override
            public Position<E> next() {
                return it.next();
            }
        };
    }

    @Override
    public Iterator<E> childrenElements(Position<E> parent) {
        TNode n = castToTNode(parent);
        return new Iterator<E>() {
            Iterator<TNode> it = n.children.elements();
            @Override
            public boolean hasNext() {
                return it.hasNext();
            }

            @Override
            public E next() {
                return it.next().elem;
            }
        };
    }

    @Override
    public int numberOfChildren(Position<E> parent) {
        TNode n = castToTNode(parent);
        return n.children.size();
    }

    @Override
    public Position<E> insertParent(Position<E> p, E o) {
        TNode n = castToTNode(p);
        TNode newP = new TNode();
        newP.elem=o;
        if (n == root){
            root = newP;
        }
        else {
            // newP takes the former role of n:
            newP.parent = n.parent;
            newP.childrenPos = n.childrenPos; // we take the position of p
            newP.parent.children.replaceElement(newP.childrenPos,newP);        
        }
        //make 'p' a child of the new position
        n.parent = newP;
        n.childrenPos = newP.children.insertFirst(n);
        size++;
        return newP;
    }

    @Override
    public Position<E> addChild(Position<E> parent, E o) {
        TNode n = castToTNode(parent);
        TNode newN = new TNode();
        newN.elem = o;
        newN.parent = n;
        newN.childrenPos = n.children.insertLast(newN);
        size++;
        return newN;
    }

    @Override
    public Position<E> addChildAt(int pos, Position<E> parent, E o) {
        TNode p = castToTNode(parent);
        if (pos > p.children.size()|| pos < 0 ) throw new RuntimeException("invalid rank");
        TNode n = new TNode();
        n.elem = o;
        n.parent = p;
        Position<TNode> linkedListPosition = null;
        if (pos == 0) linkedListPosition = p.children.insertFirst(n);
        else if (pos == p.children.size()) linkedListPosition = p.children.insertLast(n);
        else {
            Iterator<Position<TNode>> it = p.children.positions();
            // skip pos-2 nodes
            for (int i=0;i<pos-1;i++){
                it.next();
            }
            Position<TNode> lPos = (it.next()); // lPos is the LinkedList-position before the insertion point
            linkedListPosition = p.children.insertAfter(lPos, n);
        }
        n.childrenPos = linkedListPosition;
        size++;
        return n;
    }

    @Override
    public Position<E> addSiblingAfter(Position<E> sibling, E o) {
        TNode sib = castToTNode(sibling);
        if (sib == root) throw new RuntimeException("root can not have siblings!");
        TNode newN = new TNode();
        newN.elem = o;
        newN.parent = sib.parent;
        newN.childrenPos = sib.parent.children.insertAfter(sib.childrenPos,newN);
        size++;
        return newN;
    }

    @Override
    public Position<E> addSiblingBefore(Position<E> sibling, E o) {
        TNode sib = castToTNode(sibling);
        if (sib == root) throw new RuntimeException("root can not have siblings!");
        TNode newN = new TNode();
        newN.elem = o;
        newN.parent = sib.parent;
        newN.childrenPos = sib.parent.children.insertBefore(sib.childrenPos,newN);
        size++;
        return newN;
    }

    @Override
    public void remove(Position<E> p) {
        TNode n = castToTNode(p);
        if (n.children.size() != 0) throw new RuntimeException("cannot remove node with children!");
        n.creator = null;
        size--;
        if (n == root) root = null;
        else n.parent.children.remove(n.childrenPos);
    }

    public void removeSubtree(Position<E> p){
        Iterator<Position<E>> it = childrenPositions(p);
        while (it.hasNext()) removeSubtree(it.next());
        remove(p);
    }

    @Override
    public boolean isExternal(Position<E> p) {
        TNode n = castToTNode(p);
        return n.children.size()==0;
    }

    @Override
    public boolean isInternal(Position<E> p) {
        TNode n = castToTNode(p);
        return n.children.size()>0;
    }

    @Override
    public int size() {
        return size;
    }

    public void print(){
        if (root != null)
        print(root,"");
    }


    private void print(Position<E> r, String ind) {
        System.out.println(ind+r.element());
        Iterator<Position<E>> it = childrenPositions(r);
        while (it.hasNext()) print(it.next(),ind+"..");
    }


    @Override
    public E replaceElement(Position<E> p, E o) {
        TNode n = castToTNode(p);
        E old = n.elem;
        n.elem = o;
        return old;
    }

    public String toString(){
        if (size==0) return "";
        StringBuilder sb = new StringBuilder("--- begin tree ---\n");
        buildString(root,"",sb);
        sb.append("---  end tree  ---");
        return sb.toString();
    }

    private void buildString(Position<E> r, String in, StringBuilder sb) {
        sb.append(in);
        sb.append(r.element().toString());
        sb.append("\n");
        Iterator<Position<E>> it = childrenPositions(r);
        while(it.hasNext()) buildString(it.next(),".."+in,sb);
    }

    public int height(){
        return height(root);
    }

    private int height(Position<E> r) {
        int h=0;
        Iterator<Position<E>> it = childrenPositions(r);
        while(it.hasNext()){
            int height = height(it.next());
            if(height > h) h=height ;
        }
        return h+1;
    }

    public static void main(String[] args) {
        MyTree<String> t = new MyTree<String>();
        Position<String> tit = t.createRoot("Buch");
        Position<String> k1 = t.addChild(tit,"Kapitel 1");
        Position<String> k2 = t.addChild(tit,"Kapitel 2");
        Position<String> k3 = t.addChild(tit,"Kapitel 3");
        Position<String> k1u1 = t.addChild(k1,"Abschnitt 1");
        t.addChild(k1,"Abschnitt 2");
        t.addChild(k1,"Abschnitt 3");
        t.addChild(k1,"Abschnitt 4");
        t.addChild(k1,"Abschnitt 5");
//        t.print();
        System.out.println(t);
//        System.out.println(t.height());
//        System.out.println(t.maxMultPos().element());
        System.out.println(deepestPos(t).element());
    }

    static Position deepest;
    static int maxDepth;

    static Position deepestPos(Tree t){
        maxDepth=0;
        deepest = t.root();
        if (t.size() == 0) return null;
        deepestPos(t,t.root(),0);
        return deepest;
    }

    static void deepestPos(Tree t, Position p,int lev){
        if (lev > maxDepth){
            maxDepth = lev;
            deepest = p;
        }        
        Iterator<Position> it = t.childrenPositions(p);
        while(it.hasNext()) deepestPos(t,it.next(),lev+1);
    }
}