Sei sulla pagina 1di 101

Trees

Inorder Traversal
Left Root Right manner
* + +

Left + Right [Left*Right]+[Left+Right] (A*B)+[(Left*Right)+E) (A*B)+[(C*D)+E]

(A*B)+(C*D+E)

Preorder Traversal
Root Left Right manner + Left Right + [*Left Right] [+Left Right] +(*AB) [+ *Left Right E] +*AB + *C D E
+ * +

Postorder Traversal
Left Right Root manner
* + +

Left Right + [Left Right *] [Left Right+] + (AB*) [Left Right * E + ]+ (AB*) [C D * E + ]+ AB* C D * E + +

Trees Traversal
Inorder
(Left) Root (Right)
Root

Preorder
Root (Left) (Right)

Left

Right

Postorder
(Left) (Right) Root

Expression Trees
Expression tree for:

(a+b*c) +((d*e+f)*g)
Inorder traversal Preorder traversal Postorder traversal

Expression Trees
Algorithm to convert postfix expression into expression tree e.g. input: ab+cde+**

Expression Trees

BINARY SEARCH TREE (BST)

Number guessing game


Im thinking of a number between 1 and n You are trying to guess the answer For each guess, Ill tell you correct, higher or lower Describe an algorithm that minimizes the number of guesses

Binary Search Tree


For every node, X, in the tree, the values of all the keys in its left subtree are smaller than the key value of X, and the values of all the keys in its right subtree are larger than the key value of X.

Binary Search Trees


BST A binary tree where a parents value is greater than its left child and less than or equal to its right child

left (i ) < i right (i )


Why not?

left (i ) i right (i )
Can be implemented with pointers or an array

Example
12

14

20

What else can we say?


left (i ) < i right (i )
All elements to the left of a node are less than the node All elements to the right of a node are greater than or equal to the node The smallest element is the left-most element The largest element is the right-most element
20

12

14

Another example: the loner


12

Another example: the twig


12

Operations
Search(T,k) Does value k exist in tree T Insert(T,k) Insert value k into tree T Delete(T,x) Delete node x from tree T Minimum(T) What is the smallest value in the tree? Maximum(T) What is the largest value in the tree? Successor(T,x) What is the next element in sorted order after x Predecessor(T,x) What is the previous element in sorted order of x Median(T) return the median of the values in tree T

Search
How do we find an element?

Finding an element
Search(T, 9)
12

left (i ) < i right (i )

14

20

Finding an element
Search(T, 9)
12

left (i ) < i right (i )

14

20

Finding an element
Search(T, 9)
12

left (i ) < i right (i )

14

20

9 > 12?

Finding an element
Search(T, 9)
12

left (i ) < i right (i )

14

20

Finding an element
Search(T, 9)
12

left (i ) < i right (i )

14

20

Finding an element
Search(T, 13)
12

left (i ) < i right (i )

14

20

Finding an element
Search(T, 13)
12

left (i ) < i right (i )

14

20

Finding an element
Search(T, 13)
12

left (i ) < i right (i )

14

20

Finding an element
Search(T, 13)
12

left (i ) < i right (i )

14

20

Iterative search

Is BSTSearch correct?

left (i ) < i right (i )

Running time of BST


Worst case?
O(height of the tree)

Average case?
O(height of the tree)

Best case?
O(1)

Height of the tree


Worst case height?
n-1 the twig

Best case height?


floor(log2n) complete (or near complete) binary tree

Average case height?


Depends on two things: the data how we build the tree!

Insertion

Insertion

Similar to search

Insertion

Similar to search Find the correct location in the tree

Insertion

keeps track of the previous node we visited so when we fall off the tree, we know

Insertion

add node onto the bottom of the tree

Correctness?

maintain BST property

Correctness
What happens if it is a duplicate?

Inserting duplicate
Insert(T, 14)
12

left (i ) < i right (i )

14

20

Running time
O(height of the tree)

Running time
Insert(T, 15)
8 12

Height of the tree


Worst case: the twig When will this happen?

Height of the tree


Best case: complete When will this happen?

Height of the tree


Average case for random data?

Randomly inserted data into a BST generates a tree on average that is O(log n)

Visiting all nodes


In sorted order
12

14

20

Visiting all nodes


In sorted order
5
12

14

20

Visiting all nodes


In sorted order
5, 8
12

14

20

Visiting all nodes


In sorted order
5, 8, 9
12

14

20

Visiting all nodes


In sorted order
5, 8, 9, 12
12

14

20

Visiting all nodes


Whats happening?
12

5, 8, 9, 12

14

20

Visiting all nodes


In sorted order
5, 8, 9, 12, 14
12

14

20

Visiting all nodes


In sorted order
5, 8, 9, 12, 14, 20
12

14

20

Visiting all nodes in order

Visiting all nodes in order

any operation

Is it correct?

Does it print out all of the nodes in sorted order?

left (i ) < i right (i )

Running time?
Recurrence relation:
j nodes in the left subtree n j 1 in the right subtree

T (n) = T ( j ) + T (n j 1) + (1)

Or
How much work is done for each call? How many calls? (n)

What about?

Preorder traversal

12, 8, 5, 9, 14, 20 How is this useful?


12

Tree copying: insert in to new tree in preorder prefix notation: (2+3)*4 -> * + 2 3 4
14

20

What about?

Postorder traversal

5, 9, 8, 20, 14, 12 How is this useful?


12

postfix notation: (2+3)*4 -> 4 3 2 + * ?

14

20

Min/Max

12

14

20

Running time of min/max?

O(height of the tree)

Deletion

12

14

13

20

Three cases!

Deletion: case 1
No children Just delete the node
12

14

13

20

17

Deletion: case 1
No children Just delete the node
12

14

13

20

17

Deletion: case 2
One child Splice out the node
12

14

13

20

17

Deletion: case 2
One child Splice out the node
12

14

13

20

17

Deletion: case 3
Two children Replace x with its successor
12

14

13

20

17

Deletion: case 3
Two children Replace x with its successor
12

17

13

20

Deletion: case 3
Two children Will we always have a successor? Why successor?
No children Larger than the left subtree Less than or equal to right subtree

Height of the tree


Most of the operations takes time O(height of the tree) We said trees built from random data have height O(log n), which is asymptotically tight Two problems:
We cant always insure random data What happens when we delete nodes and insert others after building a tree?

Balanced trees
Make sure that the trees remain balanced!
Red-black trees AVL trees 2-3-4 trees

B-trees

Binary Search Tree Implementation

We will use a simple class that implements a binary tree to store integer values. Creating a Binary Tree We create an IntBinaryTree class.

The basic node of our binary tree has the following struct declaration. struct TreeNode { int value; TreeNode *left; TreeNode *right; } The class IntBinaryTree declaration is IntBinaryTree.h
class IntBinaryTree { private: struct TreeNode { int value; TreeNode *left; TreeNode *right; };

TreeNode *root; void destroySubTree(TreeNode *); void deleteNode(int, TreeNode *&); void makeDeletion(TreeNode *&); void displayInOrder(TreeNode *); void displayPreOrder(TreeNode *); void displayPostOrder(TreeNode *); public: IntBinaryTree() // Constructor { root = NULL; } ~IntBinaryTree() // Destructor { destroySubTree(root); } void insertNode(int); bool searchNode(int); void remove(int); void showNodesInOrder(void) { displayInOrder(root); } void showNodesPreOrder() { displayPreOrder(root); } void showNodesPostOrder() { displayPostOrder(root); } };

The root pointer is the pointer to the binary tree. This is similar to the head pointer in a linked list. The root pointer will point to the first node in the tree, or to NULL (if the tree is empty). It is initialized in the constructor. The destructor calls destroySubTree, a private member function, that recursively deletes all the nodes in the tree. Inserting a Node The code to insert a new value in the tree is fairly straightforward. First, a new node is allocated, and its value member is initialized with the new value.

Note, we assume that our binary tree will store no duplicate values.
void IntBinaryTree::insertNode(int num) { TreeNode *newNode, // Pointer to a new node *nodePtr; // Pointer to traverse the tree // Create a new node newNode = new TreeNode; newNode->value = num; newNode->left = newNode->right = NULL; if (!root) // Is the tree empty? root = newNode; else { nodePtr = root;

while (nodePtr != NULL) { if (num < nodePtr->value) { if (nodePtr->left) nodePtr = nodePtr->left; else { nodePtr->left = newNode; break; } } else if (num > nodePtr->value) { if (nodePtr->right) nodePtr = nodePtr->right; else { nodePtr->right = newNode; break; } } else { cout << "Duplicate value found in tree.\n"; break; } } } }

Program // This program builds a binary tree with 5 nodes. #include <iostream.h> #include "IntBinaryTree.h void main(void) { IntBinaryTree tree; cout << "Inserting nodes. "; tree.insertNode(5); tree.insertNode(8); tree.insertNode(3); tree.insertNode(12); tree.insertNode(9); cout << "Done.\n"; }

Program Figure shows the structure of the binary tree built by the program.

Note: The shape of the tree is determined by the order in which the values are inserted. The root node in the diagram above holds the value 5 because that was the first value inserted.

The IntBinaryTree class can display all the values in the tree using all 3 of these algorithms. The algorithms are initiated by the following inline public member functions void showNodesInOrder(void) { displayInOrder(root); } void showNodesPreOrder() { displayPreOrder(root); } void showNodesPostOrder() { displayPostOrder(root); }

Each of these public member functions calls a recursive private member function, and passes the root pointer as argument. The code for these recursive functions is simple -

void IntBinaryTree::displayInOrder(TreeNode *nodePtr) { if (nodePtr) { displayInOrder(nodePtr->left); cout << nodePtr->value << endl; displayInOrder(nodePtr->right); } }

void IntBinaryTree::displayPreOrder(TreeNode *nodePtr) { if (nodePtr) { cout << nodePtr->value << endl; displayPreOrder(nodePtr->left); displayPreOrder(nodePtr->right); } }

void IntBinaryTree::displayPostOrder(TreeNode *nodePtr) { if (nodePtr) { displayPostOrder(nodePtr->left); displayPostOrder(nodePtr->right); cout << nodePtr->value << endl; } }

Program

// This program builds a binary tree with 5 nodes. // The nodes are displayed with inorder, preorder, // and postorder algorithms. #include <iostream.h> #include "IntBinaryTree.h void main(void) { IntBinaryTree tree; cout << "Inserting nodes.\n"; tree.insertNode(5); tree.insertNode(8); tree.insertNode(3); tree.insertNode(12); tree.insertNode(9);

cout << "Inorder traversal:\n"; tree.showNodesInOrder(); cout << "\nPreorder traversal:\n"; tree.showNodesPreOrder(); cout << "\nPostorder traversal:\n"; tree.showNodesPostOrder(); }

Program Output
Inserting nodes. Inorder traversal: 3 5 8 9 12

Preorder traversal: 5 3 8 12 9 Postorder traversal: 3 9 12 8 5

As an intellectual exercise, convince yourself that for each of the 3 algorithms, the 3 output orders of the values of the 3 traversals are correct.

Searching the Tree The IntBinaryTree class has a public member function called searchNode, that returns true if a value is found in the tree, or false otherwise. The function starts at the root node, and traverses the tree, until it finds the search value, or runs out of nodes.
bool IntBinaryTree::searchNode(int num) { TreeNode *nodePtr = root; while (nodePtr) { if (nodePtr->value == num) return true; else if (num < nodePtr->value) nodePtr = nodePtr->left; else nodePtr = nodePtr->right; } return false; }

Program

// This program builds a binary tree with 5 nodes. // The SearchNode function determines if the // value 3 is in the tree. #include <iostream.h> #include "IntBinaryTree.h void main(void) { IntBinaryTree tree; cout << "Inserting nodes.\n"; tree.insertNode(5); tree.insertNode(8); tree.insertNode(3); tree.insertNode(12); tree.insertNode(9);

if (tree.searchNode(3)) cout << "3 is found in the tree.\n"; else cout << "3 was not found in the tree.\n"; }

Program Output
Inserting nodes. 3 is found in the tree.

Deleting a Node To delete a leaf node is easy a) Find its parent b) Set the child pointer that links to it to NULL c) Free the nodes memory

How to delete a node if it has child nodes? We want to delete the node, but preserve the sub-trees, that the node links to. There are 2 possible situations to be faced when deleting a non leaf node 1) The node has one child. 2) The node has two children.

The problem is not as easily solved if the node has two children.

We cannot attach both of the nodes subtrees to its parent. One solution is to a) find a position in the right subtree to attach the left subtree. b) attach the nodes right subtree to the parent

Now the code - to delete a node from the IntBinaryTree, call the public member remove. The argument passed to the function is the value of the node you want to delete.

void IntBinaryTree::remove(int num) { deleteNode(num, root); } The remove member function calls the deleteNode member function. It passes the value of the node to delete, and the root pointer. The deleteNode member function is shown below -

void IntBinaryTree::deleteNode(int num, TreeNode *&nodePtr) { if (num < nodePtr->value) deleteNode(num, nodePtr->left); else if (num > nodePtr->value) deleteNode(num, nodePtr->right); else makeDeletion(nodePtr); }

Notice the declaration of the nodePtr parameter: TreeNode *&nodePtr; nodePtr is not simply a pointer to a TreeNode structure, but a reference to a pointer to a TreeNode structure. Any action performed on nodePtr is actually performed on the argument passed into nodePtr

The reason for doing this is explained shortly. The deleteNode function uses an if/else statement.

if(num < nodePtr->value) deleteNode(num, nodePtr->left); The above statement compares the parameter num with the value member of the node that nodePtr point to. If num is less, the value being searched for will appear somewhere in the nodePtrs left subtree (if it appears at all). So recall the deleteNode function recursively, with num as the first argument, and nodePtr->left as the second argument. If num is not less than nodePtr->value, the the following else if statement is executed. else if(num > nodePtr->value) deleteNode(num, nodePtr->right);

If num is greater than nodePtr->value, then the value being searched for will appear somewhere in nodePtrs right subtree (if it appears in the tree at all). If num is equal to nodePtr->value, then neither of the if statements above will find a true condition. So, nodePtr points to the node to be deleted, and the trailing else will be executed. else makeDeletion(nodePtr); The makeDeletion function actually deletes the node from the tree and reattaches the deleted nodes sub trees.

It must have access to the actual pointer in the tree to the node that is being deleted (not just a copy of the pointer). This is why the nodePtr parameter in the deleteNode function is a reference. It must pass to makeDeletion, the actual pointer, to the node to be deleted.
void IntBinaryTree::makeDeletion(TreeNode *&nodePtr) { TreeNode *tempNodePtr; // Temporary pointer, used in // reattaching the left subtree. if (nodePtr == NULL) cout << "Cannot delete empty node.\n"; else if (nodePtr->right == NULL) { tempNodePtr = nodePtr; nodePtr = nodePtr->left; // Reattach the left child delete tempNodePtr; }

else if (nodePtr->left == NULL) { tempNodePtr = nodePtr; nodePtr = nodePtr->right; // Reattach the right child delete tempNodePtr; } // If the node has two children. else { // Move one node the right. tempNodePtr = nodePtr->right; // Go to the end left node. while (tempNodePtr->left) tempNodePtr = tempNodePtr->left; // Reattach the left subtree. tempNodePtr->left = nodePtr->left; tempNodePtr = nodePtr; // Reattach the right subtree. nodePtr = nodePtr->right; delete tempNodePtr; } }

Program // This program builds a binary tree with 5 nodes. // The DeleteNode function is used to remove two // of them. #include <iostream.h> #include "IntBinaryTree.h void main(void) { IntBinaryTree tree; cout << "Inserting nodes.\n"; tree.insertNode(5); tree.insertNode(8); tree.insertNode(3); tree.insertNode(12); tree.insertNode(9); cout << "Here are the values in the tree:\n"; tree.showNodesInOrder();

cout << "Deleting 8...\n"; tree.remove(8); cout << "Deleting 12...\n"; tree.remove(12); cout << "Now, here are the nodes:\n"; tree.showNodesInOrder(); }

Program Output
Inserting nodes. Here are the values in the tree: 3 5 8 9 12 Deleting 8... Deleting 12... Now, here are the nodes: 3 5 9

Potrebbero piacerti anche