1 trees & more tables (walls & mirrors - chapter 10 & beginning of 11)
Post on 21-Dec-2015
214 views
TRANSCRIPT
1
Trees & More Tables
(Walls & Mirrors - Chapter 10 & Beginning of 11)
2
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
3
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.
4
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.
Trees: Not Trees:root
edge
node
5
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.
6
Terminology: Example
A
B C
D EF
G H
I
J K
• 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
7
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 right subtrees of r.
• UNLIKE trees, binary trees may be empty, and they distinguish between left and right subtrees.
(These two binarytrees are distinct.)
8
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
– TL and TR are both binary search trees.
John
PeterBrenda
Amy Mary Tom
21
2
3
55
34
135
8
9
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 ) }
10
Terminology (Cont’d.)• Intuitively, a binary tree is full if it has no missing nodes.
• Formally, a binary tree of height h is full if
– It is empty (h = 0).
– Otherwise, the root’s subtrees are full binary trees of height h – 1.
• If not empty, each node has 2 children, except the nodes at level h which have no children.
• Alternatively, the binary tree has all of its leaves at level h.
11
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.
12
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.
13
Binary Tree: Pointer-Based Representationstruct TreeNode; // Binary Tree nodes are struct’stypedef 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};
14
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.
15
Binary Tree: Array-Based Representation
Index Item LeftChild
RightChild
0 Jane 1 2
1 Bob 3 4
2 Tom 5 -1
3 Alan -1 -1
4 Ellen -1 -1
5 Nancy -1 -1
6 ? -1 7
7 ? -1 8
8 ? -1 9
9 . . . . . . . . .
root
0
free
6 Jane
Alan
TomBob
NancyEllen
16
Binary Tree: Array-Based Representation
Index Item LeftChild
RightChild
0 Jane 1 2
1 Bob 3 4
2 Tom 5 -1
3 Alan -1 -1
4 Ellen -1 -1
5 Nancy 6 -1
6 Mary -1 -1
7 ? -1 8
8 ? -1 9
9 . . . . . . . . .
root
0
free
7
* Mary Added under Nancy.
Jane
Alan
TomBob
NancyEllen
Mary
17
Binary Tree: Array-Based Representation
Index Item LeftChild
RightChild
0 Jane 1 2
1 Bob 3 -1
2 Tom 5 -1
3 Alan -1 -1
4 ? -1 7
5 Nancy 6 -1
6 Mary -1 -1
7 ? -1 8
8 ? -1 9
9 . . . . . . . . .
root
0
free
4
* Ellen deleted.
Jane
Alan
TomBob
Nancy
Mary
18
Binary Tree: Array-Based Representationconst int MaxNodes = 100; // maximum size of a Binary Treetypedef 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};
19
Traversing a Binary Tree
• Preorder
• Inorder
• Postorder
20
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.
21
Preorder Traversal of a Binary Tree
Preorder Result: 60, 20, 10, 40, 30, 50, 70
60
4010
7020
30 50
1
4
5
3
2 7
6
22
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.
23
Inorder Traversal of a Binary Tree
Inorder Result: 10, 20, 30, 40, 50, 60, 70
60
4010
7020
30 50
6
4
3
1
2 7
5
24
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.
25
Postorder Traversal of a Binary Tree
Postorder Result: 10, 30, 50, 40, 20, 70, 60
60
4010
7020
30 50
7
4
2
1
5 6
3
26
Pointer-Based, Preorder Traversal in C++
// FunctionType is 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 );}
27
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 ); }}
28
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 );
29
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.
30
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 NULL leftChild pointer is found.
3) Process (visit) the item in this node.
4) Get the node’s rightChild pointer:
– If it is not NULL, 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.
31
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.
32
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
33
Table: Binary Search Tree Implementationtypedef 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 searchKeyKeyType 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);
34
Table: Binary Search Tree Implementationtypedef TableItemType TreeItemType; // TableItems are storedstruct 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};
35
Table: Binary Search Tree ImplementationAn 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};
andstruct 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.)
36
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 );
}
37
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;
}
38
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;
}
}
39
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;
}
40
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;}
41
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 );}
42
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 );}
43
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 );}
44
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 );
}
45
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
46
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 */ }}
47
Case 1: treePtr Points to a Leaf
1) Delete the leaf
2) Set treePtr to NULL
60
4010
7020
30 50
treePtr
48
Case 1: treePtr Points to a Leaf
/* delete leaf pointed to by treePtr */
delete treePtr;
treePtr = NULL;
49
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
70
3010
8020
50
treePtr
delPtr
40 60
50
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;
51
Case 3: treePtr Points to a Node with No rightChild
1) Save treePtr to delPtr
2) Set treePtr to treePtr -> leftChild
3) Delete node at delPtr
treePtr
70
6010
8020
40
delPtr
30 50
52
Case 3: treePtr Points to a Node with No rightChild
/* delete node at treePtr, making treePtr point to leftChild */
TreeNode *delPtr = treePtr;
treePtr = treePtr -> leftChild;
delPtr -> leftChild = NULL;
delete delPtr;
53
Case 4: treePtr Points to a Node with Both leftChild and rightChild
1) Let nodePtr be the TreeNode pointer that points to the leftmost descendant of treePtr -> rightChild
2) Save nodePtr -> item to treeItem
3) Save nodePtr to delPtr
4) Set nodePtr to nodePtr -> rightChild
5) Delete node at delPtr
6) Set treePtr -> item to treeItem
treeItem 50
60
90
7020
9540
50 80
treePtr
3010
nodePtr
delPtr
54
Case 4: treePtr Points to a Node with Both leftChild and rightChild
/* copy item from leftmost descendant of rightChild to node *//* at treePtr; delete leftmost descendant */
TreeItemType leftmostItem;
processLeftmost( treePtr -> rightChild, leftmostItem );
treePtr -> item = leftmostItem;
55
Table: Private Member Function Definition
/* find leftmost descendant of the node pointed to by nodePtr; *//* copy its item to treeItem, then delete it */
void Table::processLeftmost( TreeNode *&nodePtr, TreeItemType &treeItem )
{ if( nodePtr -> leftChild != NULL )
processLeftmost( nodePtr -> leftChild, treeItem ); else { treeItem = nodePtr -> item;
TreeNode *delPtr = nodePtr;
nodePtr = nodePtr -> rightChild;
delPtr -> rightChild = NULL;
delete delPtr; }}
56
Table: Public Member Function Definition
// traverses a Table in sorted search-key order, calling// function visit( ) once for each item in the Table
void Table::traverse( FunctionType visit )
{
treeTraverse( root, visit );
}
57
Table: Private Member Function Definition
// performs inorder traverse of a Binary Search Tree, calling// function visit( ) once at each node in the tree
void Table::treeTraverse( TreeNode *treePtr, FunctionType visit )
{ if( treePtr != NULL )
{
treeTraverse( treePtr -> leftChild, visit );
visit( treePtr -> item );
treeTraverse( treePtr -> rightChild, visit );
}
}
58
Treesort: Basic Idea
Problem: Sort an array of items into search-key order.
1) Insert items from the array into a Binary Search Tree (or Binary-Search-Tree-based Table).
2) Perform an inorder traversal of the Binary Search Tree, copying each item back into the array.
When step 2 is done, the array will be in sorted order!
59
Treesort
void treeSort( TableItemType a[ ], int n )
{ Table table;
// insert items from a[ ] into table
for( int i = 0; i < n; i++ )
table.insert( a[i] );
// copy items from table into a[ ] in sorted, search-key order
table.traverse( ©Item, a );
}
void copyItem( TableItemType &tableItem, TableItemType a[ ] )
{ static int k = 0;
a[k++] = tableItem;
}
60
Treesort (Cont’d.)
typedef void (*FunctionType2) (TableItemType &tableItem,
TableItemType a[ ] );
void Table::traverse( FunctionType2 visit, TableItemType a[ ] )
{ treeTraverse( root, visit, a );
}
void Table::treeTraverse( TreeNode *treePtr,FunctionType2 visit, TableItemType a[ ] );
{ if( treePtr != NULL )
{ treeTraverse( treePtr -> leftChild, visit, a );
visit( treePtr -> item, a );
treeTraverse( treePtr -> rightChild, visit, a );
}
}
61
Treesort: Efficiency
• Each insertion into a Binary Search Tree requires O( log n ) operations in the best and average cases and O( n ) operations in the worst case.
• So, performing n insertions requires O( n * log n ) operations in the best and average cases and O( n2 ) operations in the worst case.
• Traversal of the Binary Search Tree requires O( n ) operations in all cases.
• Therefore, the overall Growth Rate of Treesort is O( n * log n ) operations in the best and average cases and O( n2 ) operations in the worst case — the same as Quicksort.
• Binary-Search-Tree algorithms are efficient as long as the tree is balanced. If not balanced, Binary-Search-Tree algorithms can become as inefficient as algorithms on a linear List. We will learn about maintaining balanced trees in the coming chapters.