610 likes | 757 Views
Trees & More Tables. (Walls & Mirrors - Chapter 10 & Beginning of 11). Overview. Terminology Binary Trees Pointer-Based Representation of a Binary Tree Array-Based Representation of a Binary Tree Traversing a Binary Tree Binary Search Tree Implementation of a Table Treesort. Terminology.
E N D
Trees & More Tables (Walls & Mirrors - Chapter 10 & Beginning of 11)
Overview • Terminology • Binary Trees • Pointer-Based Representation of a Binary Tree • Array-Based Representation of a Binary Tree • Traversing a Binary Tree • Binary Search Tree Implementation of a Table • Treesort
Terminology • A tree is a collection of nodes and directed edges, satisfying the following properties: • There is one specially designated node called the root, which has no edges pointing to it. • Every node except the root has exactly one edge pointing to it. • There is a unique path (of nodes and edges) from the root to each node.
Trees: Not Trees: root edge node Graphical Representation • Trees, as defined on the preceding slide, are typically drawn with circles (or rectangles) representing the nodes and arrows representing the edges. • The root is typically placed at the top of the diagram, with the rest of the tree below it.
Terminology (Cont’d.) • If an edge goes from node a to node b, then a is called the parent of b, and b is called a child of a. • Children of the same parent are called siblings. • If there is a path from a to b, then a is called an ancestor of b, and b is called a descendent of a. • A node with all of its descendants is called a subtree. • If a node has no children, then it is called a leaf of the tree. • If a node has no parent (there will be exactly one of these), then it is the root of the tree.
A B C F D E G H I J K Terminology: Example • A is the root • D, E, G, H, J & K are leaves • B is the parent of D, E & F • D, E & F are siblings and children of B • I, J & K are descendants of B • A & B are ancestors of I subtree
(These two binary trees are distinct.) Binary Trees • Intuitively, a binary tree is LIKE a tree in which each node has no more than two children. • Formally, a binary tree is a set T of nodes such that either: • T is empty, or • T consists of a single node, r, called the root, and two (non-overlapping) binary trees, called the left and rightsubtrees of r. • UNLIKE trees, binary trees may be empty, and they distinguish between left and rightsubtrees.
21 John 3 34 Brenda Peter 55 2 8 Amy Mary Tom 5 13 Binary Search Trees • A binary search tree is a binary tree in which each node, n, has a value satisfying the following properties: • n’s value is > all values in its left subtree, TL, • n’s value is < all values in its right subtree, TR, and • TLand TRare both binary search trees.
Terminology (Cont’d.) • Intuitively, the level of a node is the number of nodes on a path from the root to the node. • Formally, level of node, n: • If n is the root of a tree, then it is at level 1. • Otherwise, its level is 1 greater than the level of its parent. • Height of binary tree, T: • If T is empty, its height is 0. • Otherwise, its height is the maximum level of its nodes, or, equivalently, 1 greater than the height of the root’s taller subtree. Namely, height(T) = 1 + max { height( TL ), height(TR ) }
Terminology (Cont’d.) • Intuitively, a binary tree is full if it has no missing nodes. • Formally, a binary tree of height h is fullif • It is empty (h = 0). • Otherwise, the root’s subtrees are full binary trees of height h – 1. • If not empty, each node has2 children, except the nodes at level h which have no children. • Alternatively, the binary tree has all of its leaves at level h.
Terminology (Cont’d.) • Intuitively, a binary tree of height h is complete if it is full down to level h – 1, and level h is filled from left to right. • Formally, a binary tree of height h is complete if • All nodes at level h – 2 and above have 2 children each, • If a node at level h – 1 has children, all nodes to its left at the same level have 2 children each, and • If a node at level h – 1 has 1 child, it is a left child.
Terminology (Cont’d.) • A binary tree is balanced if the difference in height between any node’s left and right subtree is 1. • Note that: • A full binary tree is also complete. • A complete binary tree is not always full. • Full and complete binary trees are also balanced. • Balanced binary trees are not always full or complete.
Binary Tree: Pointer-Based Representation struct TreeNode; // Binary Tree nodes are struct’s typedef string TreeItemType; // items in TreeNodes are string’s class BinaryTree { public: // declarations of public member functions private: TreeNode *root; // pointer to root of Binary Tree }; struct TreeNode // node in a Binary Tree: { // place in Implementation file TreeItemType item; TreeNode *leftChild; // pointer to TreeNode’s left child TreeNode *rightChild; // pointer to TreeNode’s right child };
Binary Tree: Array-Based Representation Basic Idea: • Instead of using pointers to the left and right child of a node, use indices into the array of nodes representing the binary tree. • Also, use variable free as an index to the first position in the array that is available for a new entry. Use either the left or right child indices to indicate additional, available positions. • Together, the list of available positions in the array is called the free list.
root 0 free 6 Nancy Ellen Alan Tom Jane Bob Binary Tree: Array-Based Representation
root 0 free 7 Nancy Mary Ellen Alan Tom Jane Bob Binary Tree: Array-Based Representation • Mary Added under Nancy.
root 0 free 4 Nancy Mary Alan Tom Jane Bob Binary Tree: Array-Based Representation • Ellen deleted.
Binary Tree: Array-Based Representation const int MaxNodes = 100; // maximum size of a Binary Tree typedef string TreeItemType; // items in TreeNodes are string’s struct TreeNode // node in a Binary Tree { TreeItemType item; int leftChild; // index of TreeNode’s left child int rightChild; // index of TreeNode’s right child }; class BinaryTree { public: // declarations of public member functions private: TreeNode node[MaxNodes]; int root; // index of root of Binary Tree int free; // index of free list, linked by rightChild };
Traversing a Binary Tree • Preorder • Inorder • Postorder
Preorder Traversal of a Binary Tree Basic Idea: 1) Visit the root. 2) Recursively invoke preorder on the left subtree. 3) Recursively invoke preorder on the right subtree.
1 7 2 3 4 5 6 60 40 10 70 20 30 50 Preorder Traversal of a Binary Tree Preorder Result: 60, 20, 10, 40, 30, 50, 70
Inorder Traversal of a Binary Tree Basic Idea: 1) Recursively invoke inorder on the left subtree. 2) Visit the root. 3) Recursively invoke inorder on the right subtree.
6 7 2 1 4 3 5 60 40 10 70 20 30 50 Inorder Traversal of a Binary Tree Inorder Result: 10, 20, 30, 40, 50, 60, 70
Postorder Traversal of a Binary Tree Basic Idea: 1) Recursively invoke postorder on the left subtree. 2) Recursively invoke postorder on the right subtree. 3) Visit the root.
7 6 5 1 4 2 3 60 40 10 70 20 30 50 Postorder Traversal of a Binary Tree Postorder Result: 10, 30, 50, 40, 20, 70, 60
Pointer-Based, Preorder Traversal in C++ // FunctionTypeis a pointer to a function with argument // (TreeItemType &) that returns void. typedef void (*FunctionType) (TreeItemType &treeItem); // Public member function void BinaryTree::preorderTraverse( FunctionType visit ) { preorder( root, visit ); }
Pointer-Based, Preorder Traversal in C++ // Private member function void BinaryTree::preorder( TreeNode *treePtr, FunctionType visit ) { if( treePtr != NULL ) { visit( treePtr -> item ); preorder( treePtr -> leftChild, visit ); preorder( treePtr -> rightChild, visit ); } }
Pointer-Based, Preorder Traversal in C++ Suppose that we define the function void printItem( TreeItemType &treeItem ) { cout << treeItem << endl; } Then, // create myTree BinaryTree myTree; // load data into myTree . . . // print TreeItems encountered in preorder traversal of myTree myTree.preorderTraverse( &printItem );
Pointer-Based,Inorder & Postorder Traversal The functions for Inorder and Postorder traversal of a binary tree are very similar to the function for Preorder traversal, and are left as an exercise.
Nonrecursive Traversal of a Binary Tree Basic Idea for a Nonrecursive, Inorder Traversal: 1) Push a pointer to the root of the binary tree onto a stack. 2) Follow leftChild pointers, pushing each one onto the stack, until a NULLleftChild pointer is found. 3) Process (visit) the item in this node. 4) Get the node’s rightChild pointer: • If it is notNULL, then push it onto the stack, and return to step 2 with the leftChild pointer of this rightChild. • If it is NULL, then pop a node pointer from the stack, and return to step 3. If the stack is empty (so nothing could be popped), then stop — the traversal is done.
Nonrecursive Traversal of a Binary Tree • The logic for a nonrecursive, preorder or postorder traversal is similar to what was presented on the previous slide for an inorder traversal. • ~14 lines of code (see chapter 10 of Walls & Mirrors) plus the code for manipulating a stack are required to implement a nonrecursive traversal. Compare this with the 5 lines of code that were required for our recursive solutions. • This example illustrates how recursion can sometimes be used to produce a clear and concise implementation.
Binary Search Tree Implementationof a Table Recall that the supported operations for an ADT Table include: • Create an empty table • Destroy a table • Determine whether a table is empty • Determine the number of items in a table • Insert a new item into a table • Delete the item with a given search key from a table • Retrieve the item with a given search key from a table • Traverse the items in a table in sorted, search-key order
Table: Binary Search Tree Implementation typedef string KeyType; // Table search-keys are strings struct dataItem // all data for an item is put into { KeyType key; // one struct for convenience // other data members are included here }; typedef dataItem TableItemType; // items in Table are dataItems // returns tableItem’s searchKey KeyType getKey( const TableItemType &tableItem ) { return( tableItem.key ); } // FunctionType is a pointer to a function with argument // (TableItemType &) that returns void, used by Table traverse( ) typedef void (*FunctionType) (TableItemType &tableItem);
Table: Binary Search Tree Implementation typedef TableItemType TreeItemType; // TableItems are stored struct TreeNode; // in TreeNodes class Table { public: // declarations of public member functions private: TreeNode *root; // root of Binary Search Tree int size; // number of items in Table }; struct TreeNode // node in a Binary Search Tree: { // place in Implementation file TreeItemType item; TreeNode *leftChild; // pointer to TreeNode’s left child TreeNode *rightChild; // pointer to TreeNode’s right child };
Table: Binary Search Tree Implementation An alternative (and perhaps, better) implementation of a Table as a Binary Search Tree would define struct dataItem { KeyType key; // other data members are included here }; and struct TreeNode { TreeItemType item; TreeNode *leftChild; TreeNode *rightChild; }; as Classes, with constructors to set the key for a dataItem and to initialize leftChild and rightChild to NULL. getKey( ) would become a public member function, providing access to key. (See chapter 10 of Walls & Mirrors for details.)
Table: Public Member Function Definitions // default constructor, which creates a new empty Table Table::Table( ) : root( NULL ), size( 0 ) { } // copy constructor, which copies origTable to a new Table Table::Table( const Table &origTable ) { copyTree( origTable.root, root ); size = origTable.size; } // destructor, which destroys a Table Table::~Table( ) { destroyTree( root ); }
Table: Private Member Function Definition // copies tree rooted at origTreePtr to a new tree rooted at newTreePtr void Table::copyTree( TreeNode *origTreePtr, TreeNode *&newTreePtr ) const { if( origTreePtr != NULL ) // copy nodes using preorder traversal { newTreePtr = new TreeNode; assert( newTreePtr != NULL ); newTreePtr -> item = origTreePtr -> item; copyTree( origTreePtr -> leftChild, newTreePtr -> leftChild ); copyTree( origTreePtr -> rightChild, newTreePtr -> rightChild ); } else newTreePtr = NULL; }
Table: Private Member Function Definition // destroys tree rooted at treePtr void Table::destroyTree( TreeNode *&treePtr ) { if( treePtr != NULL ) { // delete nodes using postorder traversal destroyTree( treePtr -> leftChild ); destroyTree( treePtr -> rightChild ); delete treePtr; treePtr = NULL; } }
Table: Public Member Function Definitions // returns true if the Table is empty, otherwise returns false bool Table::isEmpty( ) const { return size = = 0; } // returns the number of items in the Table int Table::length( ) const { return size; }
Table: Public Member Function Definitions // inserts newItem into a Table in sorted order bool Table::insert( TableItemType &newItem ) { if( treeInsertItem( root, newItem ) ) { size++; return true; } return false; } // removes item with the given searchKey from a Table bool Table::remove( KeyType searchKey ) { if( treeDeleteItem( root, searchKey ) ) { size – –; return true; } return false; }
Table: Public Member Function Definitions // retrieves a copy of item with the given searchKey from a Table bool Table::retrieve( KeyType searchKey, TableItemType &tableItem ) const { return treeRetrieveItem( root, searchKey, tableItem ); }
Table: Private Member Function Definition // inserts newItem into a Binary Search Tree rooted at treePtr bool Table::treeInsertItem( TreeNode *&treePtr, const TreeItemType &newItem ) { if( treePtr = = NULL ) { treePtr = new TreeNode; assert( treePtr != NULL ); treePtr -> item = newItem; treePtr -> leftChild = treePtr -> rightChild = NULL; return true; } if( getKey( newItem ) = = getKey( treePtr -> item ) ) return false; if( getKey( newItem ) < getKey( treePtr -> item ) ) return treeInsertItem( treePtr -> leftChild, newItem ); else return treeInsertItem( treePtr -> rightChild, newItem ); }
Table: Private Member Function Definition // retrieves a copy of item with the given searchKey from a Binary // Search Tree rooted at treePtr bool Table::treeRetrieveItem( TreeNode *treePtr, KeyType searchKey, TreeItemType &treeItem ) const { if( treePtr = = NULL ) return false; KeyType itemKey = getKey( treePtr -> item ); if( searchKey = = itemKey ) { treeItem = treePtr -> item; return true; } if( searchKey < itemKey ) return treeRetrieveItem( treePtr -> leftChild, searchKey, treeItem ); else return treeRetrieveItem( treePtr -> rightChild, searchKey, treeItem ); }
Table: Private Member Function Definition // deletes item with the given searchKey from a Binary Search Tree // rooted at treePtr bool Table::treeDeleteItem( TreeNode *&treePtr, KeyType searchKey ) { if( treePtr = = NULL ) return false; KeyType itemKey = getKey( treePtr -> item ); if( searchKey = = itemKey ) { treeDeleteNode( treePtr ); return true; } if( searchKey < itemKey ) return treeDeleteItem( treePtr -> leftChild, searchKey ); else return treeDeleteItem( treePtr -> rightChild, searchKey ); }
Delete Node at treePtr from a Tree There are 4 cases: 1) treePtr points to a leaf 2) treePtr points to a node with no leftChild 3) treePtr points to a node with no rightChild 4) treePtr points to a node with both leftChild and rightChild
Table: Private Member Function Definition // delete node at treePtr from a tree void Table::treeDeleteNode( TreeNode *&treePtr ) { if( treePtr -> leftChild = = NULL && treePtr -> rightChild = = NULL ) { /* delete leaf pointed to by treePtr */ } else if( treePtr -> leftChild = = NULL ) { /* delete node at treePtr, making treePtr point to rightChild */ } else if( treePtr -> rightChild = = NULL ) { /* delete node at treePtr, making treePtr point to leftChild */ } else { /* copy item from leftmost descendant of rightChild */ /* to node at treePtr; delete leftmost descendant */ } }
treePtr 60 40 10 70 20 30 50 Case 1: treePtr Points to a Leaf 1) Delete the leaf 2) Set treePtr to NULL
Case 1: treePtr Points to a Leaf /* delete leaf pointed to by treePtr */ delete treePtr; treePtr = NULL;
treePtr delPtr 70 30 10 80 20 50 40 60 Case 2: treePtr Points to a Node with No leftChild 1) Save treePtr to delPtr 2) Set treePtr to treePtr -> rightChild 3) Delete node at delPtr
Case 2: treePtr Points to a Node with No leftChild /* delete node at treePtr, making treePtr point to rightChild */ TreeNode *delPtr = treePtr; treePtr = treePtr -> rightChild; delPtr -> rightChild = NULL; delete delPtr;