Lecture 18:  Tree Traversals

Announcements

·      p4 (Dictionary + Document Word Count) is due 4/14

·      h6 due today; h7 posted (due )

it's often useful to iterate through a tree's nodes

o     visit each node once

e.g., to print all values, search for a value, etc.

·      four common orderings for traversing the nodes

o     preorder

o     postorder

o     level order

o     in-order (binary trees only)

 

preorder traversal ("visit the root first")

recursive definition:

1.    visit the root

2.   do a preorder traversal of the 1st subtree

3.   do a preorder traversal of the 2nd subtree

4.   do a preorder traversal of the 3rd subtree

etc., for all subtrees, left to right

example: 

 

 

 

 

 

 


preorder traversal & print:   A  B  D C  E  G F  H  I

code for preorder traversal

preorder(Treenode T) {

  if (T != null) {

    // visit T; e.g., print T

    List tmp = T.getKids();

    for (int k=0; k < tmp.size(); k++){

      preorder(tmp.get(k));

    }

  }

}

postorder traversal ("visit the root last")

1.    do a postorder traversal of the 1st subtree

2.   do a postorder traversal of the 2nd subtree

etc. for all subtrees, left to right

3.   visit the root

postorder traversal & print:  D B G E H I F C A

in-order traversal (binary trees only)

"visit the root inbetween the left and right subtrees"

1.    do an in-order traversal of the left subtree

2.   visit the root

3.   do an in-order traversal of the right subtree

 

 

 

 

 

 

 


inorder traversal & print:  D B A G E C H F I

Note:  the difference between pre- in- & post-order is

when the root is visited with respect to its subtrees

pre:  visit the root before the subtrees

in:     visit the root in between the subtrees

post: visit the root after the subtrees

level-order traversal

1.    visit all the nodes at level 1

2.   visit all the nodes at level 2

etc.  (always left to right)

 

 

 

 

 

 


·      use a Queue

(recursion for pre-/post- order uses a Stack)

 

Q.enqueue( root );

while (!Q.isEmpty()) {

  // dequeue node n

  // visit n; e.g., print n

  // enqueue all of n's children, left to right

}

(simulate the example with visit = print)

You try:

give the output for 

    pre-order, in-order, post-order & level-order traversals of

 

                                       I

             laughed                              (ha!)

       and           jumped            ate            cakes

 he               she               all           five

pre-order:  I laughed and he jumped she (ha!) ate all 5 cakes

in-order:  and he laughed she jumped I all ate 5 (ha!) cakes

post-order:  he and she jumped laughed all 5 ate cakes (ha!) I

level-order:  I laughed (ha!) and jumped ate cakes he she all 5

Binary Search Trees

BSTs

·      important special kind of binary tree

·      each node stores a key value

& maybe some associated data

·      for every node n:

o     all keys in n's left subtree are £ the key at n

o     all keys in n's right subtree are ³ the key at n

if duplicate keys are allowed, keys = the key at n 

all go in either the left or the right subtree

(but not in both – need a convention)

BSTs are important because we can do:

·      insert a key (and associated data)

·      lookup a key (return true or associated data)

·      remove a key

·      print all keys in sorted order

easily and efficiently

examples:

   each key is an int

 

 

 


  

                              YES                                         NO  (7 ! £ 6)

  

   each key is a char                          

 

 

 

 

 


                   YES                                            NO  (F not ³ G)

 

Question: 

   what kind of traversal prints the BST keys in sorted order?

answer:  an in-order traversal

Implementing BSTs

Two classes:  one for tree nodes, one for "whole tree"

class Bnode {

  public Comparable key;  // can be private - in notes

  public Bnode left, right;

  // the constructor goes here

  // get, set methods for any private fields go here

}

 

class BST {

  private Bnode root;

  public BST() { root = null; }

  public void insert( Comparable k ) throws DupEx { … }

  public boolean lookup( Comparable k ) { … }

  public void delete( Comparable k ) { … }

  public void print( PrintWriter p ) { … }

}

 

Note:  can have associated data with each node

        e.g., key = English word, associated data = French word

        how would each of the above change for associated data?

1.    Bnode has a 4th field:

public Object data;

2.   insert has two parameters:  (Comparable k, Object d)

3.   lookup returns associated data (null if key not in BST)

4.   delete might return data of deleted key

BST's lookup method

1.    if the tree is empty, return false

2.   if given key is at the root, return true

3.   if the given key is less than the value in the root,

return lookup on the left subtree

4.   if the given key is greater than the value in the root,

return lookup on the right subtree

 

want a recursive method with two parameters:  key & root

        thus, use an auxiliary method

 

public Boolean lookup(Comparable k) {

  return lookup(root,k);  // lookup is overloaded

}

 

private static Boolean lookup(Bnode n, Comparable k) {

  if (n == null) return false;   // base case

  if (n.key.equals(k)) return true; // base case

  if (k.compareTo(n.key) < 0) return lookup(n.left, k);

  else return lookup(n.right, k);

}

 

Simulate together:

 

                                                            lookup(3)

155

 
                                                            lookup(4)

                                                            lookup(9)

 


sound effects:

        empty (null):  bird;      look left:  slide up

        value found:  bell;       look right: slide down

Time for lookup

·      always follows a path from the root down

·      worst case:  goes all the way to a leaf

thus, worst case time is proportional to height of the tree

how does height relate to N = # nodes (i.e., # keys) in tree?

·      depends on the shape of the tree

o     best case:  tree is balanced

all non-leaf nodes have two children

all leaves are at depth = height

height is O(log N) where N = number of nodes

o     worst case:  tree is linear

all non-leaf nodes have just one child

height is O(N)

Summary

·      worst case time for lookup:  O(h),   h = height of tree

·      worst of the worst:  height is O(N), N = # nodes in tree

·      on average:  height is ~ log N

Note:  log N is much better than N for large N

                    N:      32    64    128     1024     1,000,000

               log N:       5      6      7          10           20

insert(k)

·      a new value is always inserted as a leaf

·      must choose position to respect BST ordering

·      attempt to insert a duplicate key is an error

algorithm:

1.    if BST is empty, make the new value be at the root

2.   else,

·      find node that will be the parent of the new node

(using "binary search")

·      create new node and make it the appropriate child of the parent