280 likes | 544 Views
COP 3538 Data Structures with OOP. Chapter 8 - Part 2 Binary Trees. Deleting a Node. Here comes some tougher stuff. Deleting a Node:. First, of course, is locating the node to be deleted. This is simple, but we now have three special cases: 1. node is a leaf – pretty easy
E N D
COP 3538 Data Structures with OOP Chapter 8 - Part 2 Binary Trees
Deleting a Node • Here comes some tougher stuff.
Deleting a Node: • First, of course, is locating the node to be deleted. • This is simple, but we now have three special cases: • 1. node is a leaf – pretty easy • 2. node has one child – not too bad. • 3. node has two children - baaad
Deleting a Node – Node has No Children • Merely change the left or right child field pointer in the parent to null. • Node will be recovered nicely (in Java) • Code is very straightforward.
Design for Deleting Nodes: Must execute a ‘find’ method that either finds the node to be deleted or does not. If node cannot be found, then typically we return a message and return to calling environment. If node is found, then one of three things must happen: • if node has no children, invoke the delete routine for this case. • If node has one child, invoke the delete routine for this case. • If node has two children, invoke the delete routine for this case.
Consider the pseudo-code driver for the Delete: Method: Delete Node Driver” Node current = theTree.find() // ‘find’ returns node or null; assigns to current if (current is null) // test to see what find() ‘returned’ return // display message; node not found. Done else if (current.left child != null && current.right child != null) invoke delete2Children // lots of fun coming! // Tests for two children if (current.left child !null || current.right child = !null) // Tests for one child invoke delete1Child else invoke delete0Children // Node to be deleted is a leaf Then: Implement the simplest one first: delete0Child() Then, implement the delete1Child() method. Then, saved the best for last.
Java code to delete node. No children. public boolean delete (int key) { Node current = root; Node parent = root; boolean isLeftChild = true; while (current.iData != key) { parent = current; // note parent is set to current before current advances left or right. if (key < current.iData) { isLeftChild = true; current = current.leftChild; find node to delete. } else { isLeftChild = false; current=current.rightChild; } if (current == null) return false; ( not found) } // end while ( drop out of loop if we do not find desired node; otherwise, do the delete()) // found node to delete } // whatever …..when current.iData = key, we will execute the delete() algorithm. // delete routine….. If no children, simply delete node: if (current.leftChild == null && current.rightChild == null) { if (current == root) // must check to see of node to be deleted is the root. root = null; delete routine else if (isLeftChild) parent.leftChild = null; else // know node has no children but we need to parent.rightChild = null; // know if node is a left or right child of parent. } // end delete code…
Deleting a Node – Node to be deleted has one Child • Node to be deleted has connection to • 1. its parent, whose pointer will need to be changed, and • 2. to the node’s single child (left or right) • Only need to link child of node to be deleted to the leftChild or rightChild of the parent, right? • Specialized case: node to be deleted is root!
Delete node F. Node to be deleted has one child A A D Note: F was a left child of parent D Want to delete node at F H G F G Leftsubtree of D now points to H and not to F after node is deleted. H H F Goes to the ‘node’ bucket…
Delete node F. Node to be deleted has one child A A Here, F has a right child D D Want to delete node at F F H G G Left subtree of D points to H and not to F H H F
Java Code: Deleting a Node – One Child // Here, node to be deleted has one child… // Know we are ‘at’ node to be deleted, // if no right child, replace parent’s pointer to this tree with left subtree. if (current.rightChild == null) // node’s left child must be moved up. if (current == root) root = current.leftChild; // But first, must code for this. else if (isLeftChild) // if node to be deleted the left child of parent parent.leftChild = current.leftChild; // ‘moved’ up… else parent.rightChild = current.leftChild; // if no left child, replace with right subtree else (if current.leftChild == null) // node’s right child must be moved up. if (current == root) root = current.rightChild; // must check for this. else if (isLeftChild) parent.leftchild = current.rightChild; else parent.rightChild = current.rightChild; // continued ahead… Delete D current=D A D F (null) rightChild of A will point to F
Deleting a Node – Two Children • This is somewhat complicated. We’ll go slowly. • Because of the way in which a binary search tree is built, we cannot simply just start moving nodes ‘up’ the tree. • Much more complicated than this. • In principle, to delete a node with two children, you need to replace the node to be deleted with its nextinordersuccessor.(Think LNR….) • This is the rule. Know this. • So, first we need to find the successor of the node to be deleted.
The Practice: Deleting a Node – Two Children – 2 • Given we have identified the node we wish to delete, we go to the node’s right child. • Think: Consider the node to be deleted at N. • We go to the Right ( L N R) • If there is a right child, the node’s rightchildwill be larger than the node to be deleted due to the way we built the binary search tree. • Then, from here, go down the tree to the lastleftchild. This will be the smallestnumber in the rightsubtree that is larger than the node to be deleted. • Notice that this node is the next inorder successor!! (and will replace the node to be deleted)
So, nextinordersuccessor of node to be deleted is either • The right successor, if this right successor has a null left subtree, • OR • The ‘left most’ successor of this right subtree, if the right successor’s left subtree is NOT null. • But there are complicated special cases…
Note: • Remember: we go ‘to the right’ because we are looking for the next inorder successor. • Given the inorder scan is L N R, this means we are effectively ‘at’ an N. • So inorder is LNR and thus (given we are at N) we ‘go to the right’ to find the nextinorder successor, that is follow the ‘R.’
Deleting a Node – Successors… Case 1: (Left subtrees don’t matter and are not shown) Let’s find the next inorder successor when the successor is the right node Delete node 38. and right node has no left child… See right: delNode = 38; its inorder successor is 72. Equivalently, the right node is clearly the successor, because its left subtree is null. -------------------------------------------------------------------- Case 2: Assume we want to delete 72. Node 90’s left subtree is NOT null. OK… We know we go to the right subtree and down to the left as far as we can go; to find 72’s next inorder successor. This would be 78. But it is still now quite simple. Swap node 78 for 72 and we are done (here) 38 72 90 78 92
Note: • Can you develop the pseudo-code for the previous explanation? • Equivalently, can you develop the pseudo-code for the previous algorithm? • Try it. It will work!
OK. New Discussion. Deleting nodes with both left and right subtrees. • So, we’ve a few cases to consider… • General rule: replace the node to be deleted with the next inorder successor. • Since a binary search tree has nodes arranged in ascending order, we are after a successor that is the smallestnext entry, that is, the smallest next higher key. • This will be the next inorder successor.
Delete where SuccessorisRight Child of delNodemeaning this successor has no left subtree. • NOTE: ‘current’ is pointing to node to be deleted! • If The Successor Is The Right Child Of Delnode, Then: • 1. Unplug current from the rightChild field of its parent and set this field pointer to the successor. (parent skips around delNode) right child of 45 will point to 88. • 2. Unplug current’s left child from current, and plug it into the leftChild field of successor. • So, whatever left subtree was of delNode 56 now is left subtree of the successor, 88. • In code: • parent.rightChild points to new successor; • Delnode’s leftChild,if one, is now new successor’s leftChild, • (So, 56’s left tree, if one, is now left tree of 88) parent 45 delNode (also current) 56 Could have a left subtree… 88 Successor - by definition, cannot have a left child.
Summarizing (book) p. 399 • Step 1: If the node to be deleted, current, is the root, it has no parent, so we merely set the root to its successor. • Recall our rule: the successor has no left subtree. So this right successor of delNode is the next inorder successor. • Step 2: We set the left child of successor (which we know is null) will now point to current’s left child (null or full sub-tree… ).
Note: successornodes - bydefinition - are guaranteed to not have a left child. • True whether the successor is the right child of the node to be deleted or one of this right child’s left children. Thesuccessor node has no left children. • So, the left subtree of delNode becomes the left subtree of the new successor node.
Examples: • But the successor CAN have a right child / right subtree, of course. • When the successor is the rightchild of the node to be deleted, the right subtree (of 88 here…) simply follows along when we move the successor. parent parent 45 45 delNode (also current) 56 88 48 48 88 100 successor (no left subtree) and any sub-tree structure that might follow 100 100
2. Delete where Successor is LeftDescendant of Right Child • If successor is a left descendant of the rightchild of • the node to be deleted (and this successor has a • right subtree…), it is a bit more complicated…(See • next slide)
These are the specific steps: Plug the right child of successor into the leftChild field of the successor’s parent. 87left = 79 Plug the right child of the node to be deleted into the rightchild field of successor. 77right = 87 Unplug current from the rightchild field of its parent and set this field to point to successor. 50right = 77 Unplug current’s left child from current and plug it into the leftChild field of successor. 77left = 62 Figure 8.20 and its algorithm; Delete from tree w/two children 50 To be deleted current 75 62 87 50 77 77 93 • Successor • (can’t have left subtree) • But it does have right subtree • Right subtree of successor • ‘replaces’ successor node. • 77 is clearly successor node • But 79 (rt child of 77) may be the root of a largesubtree. • Essentially, the right subtree of the successor becomes (replaces) • the leftchild of the parent of the successor since the parent of the • successor will no longer have a left child due to its moving up to replace delNode… 62 87 79 79 93 The code for these steps is included as four assignment statements: p.400 Clearly, the sequence is important
Note: • Please be sure you can delete a node with two children going down a ‘left’ subtree. • I have shown going down the right. But the rules will carry you through. • Remember, the successornode for the node to be deleted is the nextinordersuccessor…
Efficiency of Binary Trees • Problem with binary trees is that most nodes are at the bottom (about half) and another quarter are found in the next-to-lowest level, etc. • Given the table in your book (p. 402) you can see the number of levels to contain a given number of nodes (and these are balanced!) • So, the time needed to carry out most common tree operations is proportional to log(n) or, in Big O notation: O(log2n). • In an unorderedarray (or a linked list) of 1,000,000 items, find() will take on the average 500,000 comparisons. • In a tree of 1,000,000 items, only 20 or fewer comparisons will be required (from the log…).
If we have this 1,000,000 items (still) • Insert(): If array is ordered, can find items very quickly, but inserting them may require average 500,000 moves. • Insert(): Inserting an item in a tree with 1,000,000 items requires 20 or fewer comparisons plus a small amount of time to connect the item. • Delete: Array: Requires about 500,000 moves in an array; Tree: about 20 or fewer comparisons plus a few more comparisons to find its successor, plus a short time to disconnect the item and connect up its successor. • Therefore, a tree provides high efficiency for many common data storage operations.
Read through Trees represented as Arrays and the first page or two on Huffman Code