13.4 Deletion |
The new aspect of deletion in a PBST is that we must properly adjust parent pointers. The outline is the same as usual:
495. <PBST item deletion function 495> = void *
pbst_delete (struct pbst_table *tree, const void *item)
{ struct pbst_node *p; /* Traverses tree to find node to delete. */ struct pbst_node *q; /* Parent of p. */ int dir; /* Side of q on which p is linked. */ assert (tree != NULL && item != NULL); <Step 1: Find PBST node to delete 496> <Step 2: Delete PBST node 498> <Step 3: Finish up after deleting PBST node 503> }
This code is included in 491.
We find the node to delete by using p to search for item. For the first time in implementing a deletion routine, we do not keep track of the current node's parent, because we can always find it out later with little effort:
496. <Step 1: Find PBST node to delete 496> = if (tree->pbst_root == NULL) return NULL; p = tree->pbst_root; for (;;)
{ int cmp = tree->pbst_compare (item, p->pbst_data, tree->pbst_param); if (cmp == 0) break; dir = cmp > 0; p = p->pbst_link[dir]; if (p == NULL) return NULL; } item = p->pbst_data;
See also 497.
Now we've found the node to delete, p. The first step in deletion is to find the parent of p as q. Node p is q's child on side dir. Deletion of the root is a special case:
497. <Step 1: Find PBST node to delete 496> += q = p->pbst_parent; if (q == NULL)
{ q = (struct pbst_node *) &tree->pbst_root; dir = 0; }
The remainder of the deletion follows the usual outline:
498. <Step 2: Delete PBST node 498> = if (p->pbst_link[1] == NULL) {
<Case 1 in PBST deletion 499>
} else
{ struct pbst_node *r = p->pbst_link[1]; if (r->pbst_link[0] == NULL) {
<Case 2 in PBST deletion 500>
} else
{
<Case 3 in PBST deletion 501>
} }
This code is included in 495.
If p has no right child, then we can replace it by its left child, if any. If p does have a left child then we must update its parent to be p's former parent.
499. <Case 1 in PBST deletion 499> = q->pbst_link[dir] = p->pbst_link[0]; if (q->pbst_link[dir] != NULL) q->pbst_link[dir]->pbst_parent = p->pbst_parent;
This code is included in 498, 538, and 570.
When we delete a node with a right child that in turn has no left child, the operation looks like this:
The key points to notice are that node r's parent changes and so does the parent of r's new left child, if there is one. We update these in deletion:
500. <Case 2 in PBST deletion 500> = r->pbst_link[0] = p->pbst_link[0]; q->pbst_link[dir] = r; r->pbst_parent = p->pbst_parent; if (r->pbst_link[0] != NULL) r->pbst_link[0]->pbst_parent = r;
This code is included in 498, 539, and 571.
If p's right child has a left child, then we replace p by its successor, as usual. Finding the successor s and its parent r is a little simpler than usual, because we can move up the tree so easily. We know that s has a non-null parent so there is no need to handle that special case:
501. <Case 3 in PBST deletion 501> = struct pbst_node *s = r->pbst_link[0]; while (s->pbst_link[0] != NULL) s = s->pbst_link[0]; r = s->pbst_parent;
See also 502.
The only other change here is that we must update parent pointers. It is easy to pick out the ones that must be changed by looking at a diagram of the deletion:
Node s's parent changes, as do the parents of its new right child x and, if it has one, its left child a. Perhaps less obviously, if s originally had a right child, it becomes the new left child of r, so its new parent is r:
502. <Case 3 in PBST deletion 501> += r->pbst_link[0] = s->pbst_link[1]; s->pbst_link[0] = p->pbst_link[0]; s->pbst_link[1] = p->pbst_link[1]; q->pbst_link[dir] = s; if (s->pbst_link[0] != NULL) s->pbst_link[0]->pbst_parent = s; s->pbst_link[1]->pbst_parent = s; s->pbst_parent = p->pbst_parent; if (r->pbst_link[0] != NULL) r->pbst_link[0]->pbst_parent = r;
Finally, we free the deleted node p and return its data:
503. <Step 3: Finish up after deleting PBST node 503> = tree->pbst_alloc->libavl_free (tree->pbst_alloc, p); tree->pbst_count--; return (void *) item;
This code is included in 495.
See also: [Cormen 1990], section 13.3.
Exercises:
1. In case 1, can we change the right side of the assignment in the if statement's consequent from p->pbst_parent to q? [answer]