170 likes | 436 Views
Understand binary tree traversal methods - Depth-First & Breadth-First. Discover Preorder, Inorder, Postorder traversal; learn about Visitor pattern & Adapter classes for efficient tree traversal techniques.
E N D
Binary Tree Traversals • Binary Tree Traversal classification • Tree traversal animations • DepthFirst traversal abstraction • Accept method of AbstractTree class • Tree enumeration
Tree Traversal (Definition) • The process of systematically visiting all the nodes in a tree and performing some computation at each node in the tree is called a tree traversal. • There are two methods in which to traverse a tree: • Depth-First Traversal. • Breadth-First Traversal.
Breadth-First Traversal • The AbstractTree class breadthFirstTraversal method: 1 public void breadthFirstTraversal(Visitor visitor){ 2 Queue queue = new QueueAsLinkedList() ; 3 if(! isEmpty()) 4 queue.enqueue(this) ; 5 while(! queue.isEmpty() && ! visitor.isDone()) { 6 Tree head = (Tree) queue.dequeue() ; 7 visitor.visit(head.getKey()) ; 8 for(int i = 0; i < head.getDegree(); i++) 8 { 9 Tree child = head.getSubtree(i) ; 10 if(! child.isEmpty()) 11 queue.enqueue(child) ; 12 } 13 } 14 }
H D L F N B J A E I C G K M O Preorder Traversal N-L-R
H D L F N B J A E I C G K M O Inorder Traversal L-N-R
H D L F N B J A E I C G K M O Postorder Traversal L-R-N
H D L F N B J A E I C G K M O Breadth-First Traversal
Depth-First Traversal Abstraction • Preorder, inorder, and postorder traversals are special cases of depth-first traversal. • Rather than implement each of these traversals directly, as we have done in page 3, we make use of a design pattern, called adapter, that allows the three traversals to be implemented as the following single method: 1 public class BinaryTree extends AbstractTree{ 2 protected Object key ; 3 protected BinaryTree left ; protected BinaryTree right ; 4 public void depthFirstTraversal(PrePostVisitor visitor){ 5 if(! isEmpty()){ 6 visitor.preVisit(key) ; 7 left.depthFirstTraversal(visitor) ; 8 visitor.inVisit(key) ; 9 right.depthFirstTraversal(visitor) ; 10 visitor.postVisit(key) ; } 11 } 12 // . . . 13 }
Breadth-First Traversal Abstraction (Contd.) • The depthFirstTraversal method takes as its argument any object that implements the PrePostVisitor interface: 1 public interface PrePostVisitor{ 2 void preVisit(Object object) ; 3 void inVisit(Object object) ; 4 void postVisit(Object object) ; 4 boolean isDone() ; • } • As each node is visited in a traversal, the preVisit, inVisit, and postVisit methods of the visitor are invoked on the object contained in that node. • Recall that to print all objects in a container, we can create a PrintingVisitor that prints every object it visits: 1 public class PrintingVisitor implements Visitor{ 2 public void visit(Object object){ 4 System.out.println(object) ; } // . . . 5 // . . . } 6 // . . . 7 Container c = new SomeContainer( ) ; 8 c.accept(new PrintingVisitor( )) ;
Breadth-First Traversal Abstraction (Contd.) • Unfortunately a PrintingVisitor instance cannot be passed to the depthFirstTraversalmethod: • The depthFirstTraversal method expects an object that implements the PrePostVisitor interface, whereas the PrintingVisitor class implements the Visitor interface. • The solution is to use an adapter. An adapter converts the interface provided by one class to the interface required by another class. • For example, if we want a preorder traversal, then the call to the preVisit (made by the depthFirstTraversal) should be mapped to the visit method (provided by the PrintingVisitor). • Similarly, a postorder traversal is obtained by mapping postVisit to visit. • An inorder traversal is obtained by mapping inVisit to visit.
AbstractPrePostVisitor class • The AbstractPrePostVisitor class defined below provides default implementations for the preVisit, inVisit, and postVisit methods: 1 public abstract class AbstractPrePostVisitor implements PrePostVisitor 2 { 3 public void preVisit(Object object) { } 4 public void inVisit(Object object) { } 5 public void postVisit(Object object) { } 6 public boolean isDone() { return false;} 7 } • Each of the three adapter classes we will discuss: PreOrder, InOrder, and PostOrder: (a) Extends the AbstractPrePostVisitor class. (b) Has a single, protected, instance variable that implements the Visitor interface. (c) Overrides the isDone method and only one of the three methods, preVisit, inVisit, and postVisit, inherited from the AbstractPrePostVisitor class.
Adaptor classes 1 public class PreOrder extends AbstractPrePostVisitor{ 2 protected Visitor visitor ; 3 public PreOrder(Visitor visitor) 4 { this.visitor = visitor ; } 5 public void preVisit(Object object) 6 { visitor.visit(object) ; } 7 public boolean isDone(){ 8 return visitor.isDone() ; } 9 } 1 public class InOrder extends AbstractPrePostVisitor{ 2 protected Visitor visitor ; 3 public InOrder(Visitor visitor) 4 { this.visitor = visitor ; } 5 public void inVisit(Object object) 6 { visitor.visit(object) ; } 7 public boolean isDone() 9 { return visitor.isDone() ; } 8 }
Adaptor classes (Contd.) 1 public class PostOrder extends AbstractPrePostVisitor{ 2 protected Visitor visitor ; 3 public PostOrder(Visitor visitor) 4 {this.visitor = visitor ; } 5 public void postVisit(Object object) 6 { visitor.visit(object) ; } 7 public boolean isDone() 9 { return visitor.isDone() ; } 10 } • Each adapter class provides a different interface mapping. • Note that an adapter class provides no functionality of its own, it simply forwards method calls to the visitor instance as required. • The following code illustrates how these adapters are used: Visitor v = new PrintingVisitor() ; BinaryTree t = new BinaryTree() ; // . . . t.depthFirstTraversal(new PreOrder(v)) ; t. depthFirstTraversal(new InOrder(v)) ; t.depthFirstTraversal(new PostOrder(v)) ;
The default accept method of the AbstractTree class • Usually the accept method of a container is allowed to visit the elements of the container in any order. • A tree traversal visits the nodes in either preoder or postorder and for Binary trees inorder traversal is also possible. • Hence, the AbstractTree class provides a default accept method that does a preorder traversal: 1 public void accept(Visitor visitor) 2 { 3 depthFirstTraversal(new Preorder(visitor)) ; 4 }
The Default Tree Enumeration • The implementation of a tree enumeration must do a tree traversal. • Hence, the AbstractTree class provides a default tree enumeration that does a preorder traversal. The enumeration is implemented as an inner class: 1 protected class TreeEnumeration implements Enumeration{ 2 protected Stack stack; 3 public TreeEnumeration(){ 4 stack = new StackAsLinkedList() ; 5 if(! isEmpty()) stack.push(AbstractTree.this) ; 6 } 7 public boolean hasMoreElements() { return ! stack.isEmpty(); } 8 public Object nextElement(){ 9 if(stack.isEmpty()) throw new NoSuchElementException() ; 10 Tree top = (Tree) stack.pop() ; 11 for(int i = top.getDegree() - 1 ; i >= 0 ; i--){ 12 Tree subtree = (Tree) top.getSubtree(i) ; 13 if(! subtree.isEmpty()) stack.push(subtree) ; 14 } 15 return top.getKey( ); } 16 }
Using a Tree Enumeration • The getEnumeration method of the AbstractTree class returns a new instance of the TreeEnumeration inner class each time it is called: 1 public Enumeration getEnumeration( ) 2 return new TreeEnumeration( ) ; • } • The following program fragment shows how to use a tree numeration: 1 Tree tree = new BinaryTree() ; 2 // . . . 3 Enumeration e = tree.getEnumeration() ; 4 while(e.hasMoreElements() 5 { 6 Object obj = e.nextElement() ; 7 System.out.print(obj + " ") ; 8 }