Tree representation and tree search - Ed. 2. and 3.: Chapter 6 - Ed. 4.: Chapter 10 Data Structures for Representing Trees Recall that we can use arrays or linked lists to implement sequences. B T c b 1 2 3 How can a tree be represented in a program? 4 Since a binary tree is an ordered tree and has levels, it is convenient to assign a number to each node. 1 2 3 4 8 5 9 10 6 11 12 7 13 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 14 15 Formally, for every node v of a tree T, let p(v) be the integer defined as follows. If v is the root of T, then p(v) = 1. If v is the left child of the node u, then p(v) = 2p(u) If v is the right child of the node u, then p(v) = 2p(u) + 1. 2 u p(u)=2 2p(u)=4 p(u)=4 2p(u)+1=5 4 5 2p(u)+1=9 2p(u)=8 8 8 4 9 10 9 11 The function p() is called a level numbering of the nodes in a binary tree T. In general, the numbers for nodes may not be consecutive when the number of nodes at some level does not reach its maximum. 1 2 3 4 5 10 7 11 14 15 With a level numbering of the nodes, it is convenient to use a vector S to represent a tree. Recall that we use ranks to access elements in a vector. So we can place the node v in a tree T at rank p(v). 1 2 3 4 5 10 1 2 3 7 11 4 5 14 7 10 11 15 14 15 We can also use a linked structure to represent a tree. In this case, the node v of a tree T is represented by an object with four references. parent left right element providence root Chicago Seattle 5 size Baltimore Baltimore Chicago New York Providence Seattle New York Class BTNode public class BTNode implements Position { private Object element; private BTNode left, right, parent; public BTNode() {} public BTNode( Object o, BTNode u, BTNode v, BTNode w ) { setElement( o ); setParent( u ); setLeft( v ): setRight( w ); } public Object element() { return element; } public void setElement( Object o ) { element = o; } public BTNode getLeft() { return left; } public void setLeft( BTNode v ) { left = v; } public BTNode getRight() { return right; } public void setRight( BTNode v ) { right = v; } public BTNode getParent() { return parent; } public void setParent( BTNode v ) { parent = v; } } Interface Hierarchy for Positions Position element(); DNode element(){…}; getNext(){…}; getPrev(){…}; setNext(){…}; setPrev(){…}; setElement(){…}; BTNnode element(){…}; getLeft(){…}; getRight(){…}; setLeft(){…}; setRight(){…}; getParent(){…}; setElement(){…}; Class LinkedBinaryTree Additional methods are supported. expandExternal(v): Transform the external node v into an internal node by creating two new external nodes and making them the left and right children of v, respectively; an error condition occurs if v is an internal node. Input: Position; Output: None v v removeAboveExternal(w): Remove the external node w together with its parent v, replacing v with the sibling of w; this operation generates an error condition if w is an internal node or w is the root. Input: Position; Output: None v z z w public class LinkedBinaryTree implements BinaryTree { private Position root; private int size; public LinkedBinaryTree() { root = new BTNode(null, null, null, null); size = 1; } public int size() { return size; } public boolean isEmpty() { return ( size == 0 ); } public boolean isInternal( Position v ) { return ((( BTNode )v ).getLeft() != null && (( BTNode )v ).getRight() != null ); } public boolean isExternal( Position v ) { return ((( BTNode )v ).getLeft() == null && (( BTNode )v ).getRight() == null ); } public boolean isRoot( Position v ) { return ( v == root()); } public Position root() { return root; } public ArrayPositionIterator positions() { Position [] positions = new Position[ size() ]; inorderPositions( root(), positions, 0 ); return new ArrayPositionIterator( positions ); } Here inorderPositions() returns inorder positions in the array positions. The constructor ArrayPositionIterator() creates an object of array-based position iterator. public Position leftChild( Position v ) { return (( BTNode )v ).getLeft(); } public Position rightChild( Position v ) { return (( BTNode )v ).getRight(); } public Position sibling( Position v ) { Position p = parent( v ); Position lc = leftChild( p ); if( v == lc ) return rightChild( p ); else return lc; } p=paren(v) lc=leftChild(p) v rightChild(p) public Position parent( Position v ) { return (( BTNode )v ).getParent(); } public Object replaceElement( Position v, Object o ) { Object temp = (( BTNode )v ).element(); (( BTNode )v ).setElement( o ); return temp; } public void swapElements( Position v, Position w ) { Object temp = w.element(); (( BTNode )w ).setElement( v.element()); (( BTNode )v ).setElement( temp ); } public void expandExternal( Position v ) { if( isExternal( v )) { (( BTNode )v ).setLeft( new BTNode( null, ( BTNode )v, null, null )); (( BTNode )v ).setRight( new BTNode( null, ( BTNode )v, null, null )); size += 2; } } v v public void removeAboveExternal( Position v ) { if( isExternal( v )) { BTNode p = ( BTNode )parent( v ); BTNode s = ( BTNode )sibling( v ); if( isRoot( p )) { s.setParent( null ); root = s; } else { BTNode g = ( BTNode )parent( p ); if( p == leftChild( g )) g.setLeft( s ); else g.setRight( s ); s.setParent( g ); } size -= 2; } public Iterator elements() { } } g p s g s v public class ArrayPositionIterator implements Iterator { protected Position a[]; // the underlying array protected Position cur; int i = 0; // the current (next) position public ArrayPositionIterator() { } // default constructor public ArrayPositionIterator(Position[] L) { // preferred constructor a = L; if (a[0] == null) cur = null; // array is empty else cur = a[i]; // start with the first position } public boolean hasNext() { return (cur != null); } public Position next() throws NoSuchElementException { if (!hasNext()) throw new NoSuchElementException("No next position"); Position toReturn = cur; if (cur == a[i+1]) cur = null; // no positions left else cur = a[i+1]; i++; // move cursor to the next position return toReturn; } } protected void inorderPosition (Position v, Positions[] pos, int i) throws InvalidPositionException { if (hasLeft(v)) inorderPosition(left(v), pos, i); // recurse on left child pos[i] := v; i := i + 1; //if (((BTPosition)v).element() instanceof Integer) // System.out.print(((Integer)((BTPosition)v).element()).intValue() + " "); //else System.out.print(((Character)((BTPosition)v).element()).charValue() + " "); if (hasRight(v)) inorderPosition(right(v), pos, i); // recurse on right child } - / + * + 3 + - 3 1 9 * 2 - 3 5 6 7 4 (((( 3 + 1 ) * 3 ) / (( 9 - 5 ) + 2 )) - (( 3 * ( 7 - 4 )) + 6 )) Each node has a value associated with it. If a node is external, then its value is that of its variable or constant. If a node is internal, then its value is defined by applying its operation to the values of its children. IspectableContainer size isElement Elements IspectablePositionContainer positions PositionContainer swapElement replaceElement Tree InspectableTree root, parent, children, isRoot isInternal, isExternal InspectableBinaryTree leftChild, rightChild, sibling BinaryTree imple. LinkedBinaryTree … …, replaceElement, swapElement, expandExternal, removeAboveExternal A Linked Structure for General Trees We can extend the linked structure for binary tree to represent general trees. parent element childrenContainer An object of node has three references: Parent, childrenContainer and element. Example: New York Baltimore Chicago Providence Seattle Data Structure Exercises 12.1 Preorder Traversal r T Recall that we can visit the nodes in a binary tree with inorder traversal. What if the tree is not a binary tree? u v Are there any other ways to visit the nodes systematically? Assume we have a tree T (not necessarily a binary tree). preorder traversal of T: The root of T is visited first and then the subtrees rooted at its children are traversed recursively. If the tree is ordered, then the subtrees are traversed according to the order of the children. The action of visiting a node: When we visit each node of T, what do we do? We can do anything that makes sense such as incrementing a counter or some complex computation based on the element at each node. Example: Preorder traversal of a book structure Paper Title Abstract 1.1 Ch. 1 1.2 Ch. 2 2.1 2.2 Ch. 3 2.3 3.1 3.2 References Algorithm preorder(T,v): perform the “visit” action for node v for each child w of v call preorder(T,w) v postorder(T,w) w postorder(T,v) A Java implementation of preorder traversal for printing the elements. In this case, the action of visiting a node is printing the element. public static String preorderPrint( InspectableTree T, Position v ) { String s = v.element().toString(); PositionIterator children = T.children( v ); while( children.hasNext()) s += " " + preorderPrint( T, children.nextPosition()); return s; } Example: What is the string that preorderPrint(T,v) returns? Sales Domestic Canada v International S. America Overseas After visiting the node v: s = “Sales” + “ ” + s1 + “ ” + s2 Sales Domestic Canada v International S. America Overseas s = “Sales” + “ ” + s1 + “ ” + s1 = “Domestic” Sales Domestic Canada v International S. America Overseas s2 s = “Sales Domestic” + “ ” + s2 s2 = “International” + “ ” + s21 + “ ” + s22 + “ ” + s23 Sales Domestic Canada v International S. America Overseas s = “Sales Domestic International” + “ ” + s21 + “ ” s22 + “ ” s23 s21 = “Canada” Sales Domestic Canada v International S. America Overseas s = “Sales Domestic International Canada” + “ ” +s22 + “ ” + s23 s22 = “S.America” Sales Domestic Canada v International S. America Overseas s = “Sales Domestic International Canada S.America” + “ ” + s23 s23 = “Oversea” Sales Domestic Canada v International S. America Overseas s = “Sales Domestic International Canada S.America Oversee” Preorder Traversal of a Binary Tree Since a binary tree is just a special case of trees, the algorithm for preorder traversal of a binary tree is very similar. Algorithm binaryPreorder(T,v): perform the “visit” action for node v if v is an internal node call binaryPreorder(T, T.leftChild( v )) call binaryPreorder(T, T.rightChild( v )) Preorder Traversal Using Stack S.push(root); preorder While (S is not empty) do { x := S.pop( ); access x; let x1, …, xk be the children of x; for i = k to 1 do {S.push(xi);} } traversal breadth-first Q.enqueue(root); While (Q is not empty) do { x := Q.dequeue( ); access x; let x1, …, xk be the children of x; for i = 1 to k do {Q.enqueue(xi);} } traversal Load a tree from disk into main memory File: a b e c f g d a; b, c, d. b; e, f. e; f; c; g. g; d; a b c a d b e e f g public class Node1 { String x; Node2 y; } public class Node2 { Node1 x; Node2 y; } f c g d Access 0th in the file to find the root of the tree; S.push(root, null); while (S is not empty) do { x := S.pop( ); generate a node n for x.node_info; if x.point_to_parent is not null then generate links between n and x.pointer_to_parent; Access the corresponding line in the file to find the children of x; let x1, …, xk be the children of x; a; b, c, d. for j = k to 1 do b; e, f. S.push(xj, n); } e; stack S: node_info Pointer_to_parent f; c; g. g; d; (*Assume that the nodes are stored in preorder in the file.*) i := 0; Access 0th line in the file to find the root of the tree; S.push(root, null); while (S is not empty) do { x := S.pop( ); generate a node n for x.node_id; if x.point_to_parent is not null then generate links between n and x.pointer_to_parent; Access the ith line in the file to find the children of x; let x1, …, xk be the children of x; for j = k to 1 do S.push(xj, n); i := i + 1; } XML File <book> <title> “The Art of Programming” </title> <author> “D. Knuth” </author> <year> “1969” </year> </book> <book> <title> <author> “The Art of “D. Knuth” Programming” <year> “1969” XML File Read a file into a character array A: < b o o k > < t i t l e > “ T h e stack S: node_value Pointer_to_node A r t … XML File Algorithm: Scan array A; If A[i] is ‘<’ and A[i+1] is a character then { generate a node x for A[i.e.], where A[j] is ‘>’ directly after A[i]; let y = S.top().pointer_to_node; make x be a child of y; S.push(A[i..j], x); If A[i] is ‘ ‘‘ ’, then { genearte a node x for A[i.e.], where A[j] is ‘ ’’ ’ directly after A[i]; let y = S.top().pointer_to_node; make x be a child of y; If A[i] is ‘<’ and A[i+1] is ‘/’, then S.pop();