Trees & More Tables - Department of Computer Science • NJIT

Download Report

Transcript Trees & More Tables - Department of Computer Science • NJIT

Trees & More Tables
(Walls & Mirrors - Chapter 10 & Beginning of 11)
1
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
2
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.
3
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
4
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.
5
Terminology: Example
A
B
C
F
D
E
G
I
J
subtree
K
H
• 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
6
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 (nonoverlapping) 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 binary
trees are distinct.)
7
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.
21
John
3
Brenda
Peter
2
Amy
34
Mary
55
8
Tom
5
13
8
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 ) }
9
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.
10
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.
11
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.
12
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
};
13
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.
14
Binary Tree: Array-Based Representation
root
Index Item Left Right
Child Child
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
...
...
...
0
free
Jane
6
Bob
Alan
Ellen
Tom
Nancy
15
Binary Tree: Array-Based Representation
root
Index Item Left Right
Child Child
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
...
...
...
* Mary Added under Nancy.
0
free
Jane
7
Bob
Alan
Tom
Ellen
Nancy
Mary
16
Binary Tree: Array-Based Representation
root
Index Item Left Right
Child Child
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
...
...
...
* Ellen deleted.
0
free
Jane
4
Bob
Tom
Alan
Nancy
Mary
17
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
};
18
Traversing a Binary Tree
• Preorder
• Inorder
• Postorder
19
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.
20
Preorder Traversal of a Binary Tree
1
60
7
2
20
3
70
4
10
5
30
40
6
50
Preorder Result: 60, 20, 10, 40, 30, 50, 70
21
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.
22
Inorder Traversal of a Binary Tree
6
60
7
2
20
1
70
4
10
3
30
40
5
50
Inorder Result: 10, 20, 30, 40, 50, 60, 70
23
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.
24
Postorder Traversal of a Binary Tree
7
60
6
5
20
1
70
4
10
2
30
40
3
50
Postorder Result: 10, 30, 50, 40, 20, 70, 60
25
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 );
}
26
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 );
}
}
27
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 );
28
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.
29
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.
30
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.
31
Binary Search Tree Implementation
of 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
32
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);
33
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
};
34
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.)
35
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 );
}
36
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;
}
37
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;
}
}
38
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;
}
39
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;
}
40
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 );
}
41
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 );
}
42
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 );
}
43
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 );
}
44
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
45
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
*/ }
}
46
Case 1: treePtr Points to a Leaf
60
20
10
70
40
30
treePtr
50
1) Delete the leaf
2) Set treePtr to NULL
47
Case 1: treePtr Points to a Leaf
/* delete leaf pointed to by treePtr */
delete treePtr;
treePtr = NULL;
48
Case 2: treePtr Points to a Node with No leftChild
70
20
80
treePtr
10
30
delPtr
1) Save treePtr to delPtr
2) Set treePtr to treePtr -> rightChild
3) Delete node at delPtr
50
40
60
49
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;
50
Case 3: treePtr Points to a Node with No rightChild
70
20
80
treePtr
10
60
1) Save treePtr to delPtr
2) Set treePtr to treePtr -> leftChild
3) Delete node at delPtr
40
30
delPtr
50
51
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;
52
Case 4: treePtr Points to a Node with Both
leftChild and rightChild
treePtr 90
40
20
10
nodePtr
30
delPtr
95
70
50
80
60
treeItem 50
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
53
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;
54
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;
}
}
55
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 );
}
56
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 );
}
}
57
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!
58
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( &copyItem, a );
}
void copyItem( TableItemType &tableItem, TableItemType a[ ] )
{ static int k = 0;
a[k++] = tableItem;
}
59
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 );
}
}
60
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
2
the best and average cases and O( n ) 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 )
2
operations in the best and average cases and O( n ) 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.
61