Tree ADT

Tree

Tree: Set of nodes connected by edges that indicate the relationships among the nodes.

Two Forms:

  1. Binary
  2. General.

Terminology

Traversal of a Tree

Traversal: Process of processing each node exactly once.

In general, to iterate through a tree, we need to use a stack or recursion.

Binary Trees

Binary Tree: Each node can have between 0—2 nodes.

Types of Binary Trees:

Height: \boxed{ \text{Height of a Full Binary Tree} = 2^\text{Number of Nodes} - 1 }

Importance: Many things can be represented with binary trees, which can make operations O \log (n)

Traversing a Binary Tree

Steps:

  1. Visit the root.
  2. Visit all nodes in the root’s left subtree.
  3. Visit all nodes in the root’s right subtree.

Ways to Traverse a Binary Tree:

  1. Preorder: Visit root before we visited root’s subtrees.
  2. Inorder: Visit root between visiting nodes in root’s subtrees.
  3. Postorder: Visit root after visiting nodes in root’s subtrees.
  4. Level-Order: Begin at root and visit nodes one level at a time.

Note: Given preorder and inorder traversal, or postorder and inorder traversal, you can reconstruct the tree.

Traversing a General Tree

Note: There is no inorder traversl because there’s no concept of left and right.

Ways to Traverse a General Tree:

  1. Level-Order: Begin at root and visit nodes one level at a time.
  2. Preorder: Visit all parents before children.
  3. Postorder: Visit all children before parents.

Note: Data Structure for Traversal

Interface for All Trees

This interface contains basic methods for both tree types:

public interface TreeInterface<T> {
    T getRootData()
    int getHeight();
    int getNumberOfNode();
    boolean isEmpty();
    void clear();
}

This interface contains all four iterators:

import java.util.Iterator;
public interface TreeIteratorInterface<T> {
    Iterator<T> getPreorder();
    Iterator<T> getPostorder();
    Iterator<T> getInorder();
    Iterator<T> getLevelOrder();
}

Interface for Binary Tree

public interface BinaryTreeInteface<T> extends TreeInterface<T>, TreeIteratorInterface<T> {
    void setRootData(T rootData);
    void setTree(T rootData, BinaryTreeInterface<T> leftTree, BinaryTreeInterface<T> rightTree);
}

Example: Manually building a tree

// Build leaves
BinaryTreeInterface<String> bTree = new BinaryTree<>();
bTree.setTree("B", null, null);

BinaryTreeInterface<String> cTree = new BinaryTree<>();
cTree.setTree("C", null, null);

BinaryTreeInterface<String> emptyTree = new BinaryTree<>();

// Form larger subtree (root B and C to A)
BinaryTreeInterface<String> aTree = new BinaryTree<>();
aTree.setTree("A", bTree, cTree);

Binary Tree Applications

Expression Tree

Expression Tree: Binary tree representation of an expression.

Important: Things to Know when Recreating Expression Trees from their Traversals

Example: Evaluating an expression tree

Algorithm evaluate(expressionTree) {
  if expressionTree is a leaf node {
      # (l and r children == null)
      return the value of the leaf node;
  } else {
      firstOperand = evaluate(left subtree)
      secondOperand = evaluate(right subtree)
      operator = root of expression tree
      return firstOperand operator secondOperator
  }
}

Decision Tree

Real-World Example: Expert systems with decision trees

Example: Interface for a decision tree.

public interface DecisionTreeInterfacemT< extends BinaryTreeInterface<T> {
  T getCurrentData();
  void setCurrentData(T newData);
  void setResponses(T responseForNo, T responseForYes);
  boolean isAnswer();
  void advanceToNo();
  void advanceToYes();
  void resetCurrentNode();
}

Note the hierarchy of inheritance:

  1. Tree
  2. Binary Tree
  3. Decision Tree

Binary Search Tree

Binary Search Tree: A binary tree where each node’s value is:

  1. Greater than all nodes in its left subtree, and
  2. Less than all nodes in its right subtree.

Cool Characteristic: In-Order Traversal of BST

On Performance: \boxed{ \text{(Balanced) BST Search Performance: } O ( \log n ) } \\ \small\textit{(where $n$ is the height of the tree)}

Example: How-To Search a BST

Algorithm bstSearch(tree, needle) {
  if tree is empty
      return false
  else if needle == root
      return true
  else if needle < root
      return bstSearch(tree's left subtree, needle)
  else needle > root
      return bstSearch(tree's right subtree, needle)
}

Heap ADT

Heap: A complete binary tree where each node is (1) smaller or (2) larger than all objects in its descendants.

Two Types of Heaps:

  1. Maxheap: Object in node greater than or equal to its descendent objects.
  2. Minheap: Object in node less than or equal to its descendent objects.

Example: Maxheap Interface

public interface MaxHeapInterface<T extends Comparable<? super T>> {
  void add (T newEntry);
  T removeMax();
  T getMax();
  boolean isEmpty();
  int getSize();
  void clear();
}

Implementation Note: We usually implement heaps with arrays.

Cool Characteristic: Behaves like a priority queue

General Tree Applications

Parse Tree:

Real-World Examples of Parse Trees:

Relationship between Binary Tree and General Tree:

Tree Implementations

Binary Tree

Binary Tree Nodes

Each node has three fields:

  1. Value
  2. Left node
  3. Right node
class BinaryNode<T> {
    private T data;
    private BinaryNode<T> leftChild;
    private BinaryNode<T> rightChild;
    public BinaryNode() {
        this(null);
    }
    public BinaryNode(T dataPortion) {
        this(dataPortion, null, null);
    }
    public BinaryNode(T dataPortion, BinaryNode<T> leftChild, BinaryNode<T> rightChild) {
        this.data = dataPortion;
        this.leftChild = leftChild;
        this.rightChild = rightChild;
    }

}

initializeTree

private void initializeTree(T rootData, BinaryTree<T> leftTree, BinaryTree<T> rightTree)

Additional Challenges

  1. Make sure you copy nodes by value when applicable.
  2. If you want to use recursion for a public method; you’ll need a private and public version of a method.

Example: In-order Traversal

Recursive:

private void inorderTraverse(BinaryNode<T> node) {
    if (node != null) {
        inorderTaverse(node.getLeftChild());
        System.out.println(node.getData());
        inorderTaverse(node.getRightChild());
    }
}

Stack:

  1. Push node to stack.
  2. Push left node onto stack
  3. Pop off leaf node and parent of the leaf node.
  4. Push right node onto stack

Example: Level-Order Traversal

Use a queue.

  1. Add everything on the current level to the queue.
  2. Dequeue everything once you reach end of level.
  3. Go to next level.

Expression Tree

Expression Tree Implementation

public interface ExpressionTreeInterface extends BinaryTreeInterface<String> {
    double evaluate();
}

Postfix \to Expression Tree (BTS)

Steps: For each token:

  1. If token is an operand:
  2. If token is an operator:

Example: Manually converting postfix to expression tree

Prefix: ab+

We will use a binary tree stack, a stack of binary trees.

  1. We will read the first character onto the stack as a leaf node (a)
  2. We will add the second character onto the stack as a leaf node (b)
  3. Once we read an operator, we will pop off the two leaf nodes and make them children of the operator (+)

Evaluation

private double evaluate(BinaryNode<String> rootNode) {
    double result;
    if (rootNode == null) {
        return 0;
    }
    if (rootNode.isLeaf()) {
        String variable = rootNOdegetData();
        result = getValueOf(Variable);
    } else {
        double firstOperand = evaluate(rootNode.getLeftChild());
        double secondOperand = evaluate(rootNode.getRightChild());
        /* Perform computation */
    }
}

General Trees

You can implement a general tree with (1) lists, or (2) a binary tree!

Representing General Tree with a Binary Tree:

Tip: Draw the binary with horizontal right-lines to make the sibling relationship clearer.