Integer remove(Key k) { Entry e; Node p; /* ----------------------- Find k to remove ----------------------- */ e = keySearch(k); /* ======================= Check if k exists... ======================= */ if ( e == null ) { return(null); // Not found, nothing to delete... } /* ========================= We found the entry =============== */ Entry Old = e; // Remember the old value (to return it) /* ------------------------------ Step 1: remove entry e ------------------------------ */ p = node that contains entry e; if ( p == a leaf node ) { Delete entry e from p; // Note: this requires moving array elements } else /* Delete entry from internal node */ { /* ------------------------------- Replace e with e's successor ------------------------------- */ succ = successor entry of the deleted entry e; q = node that contains the succ entry; Replace entry e (in node p) with "succ" entry; Delete the succ entry from node q; // Note: this requires moving array elements p = q; } /* *********************************************** Handle a possible underflow situation *********************************************** */ if ( p is empty ) { handleUnderflow(p, null); // This is a recursive routine ! // The second parameter is a subtree that hangs under // the underflow node (the red reference in my figures) } return old.value; // Return } |
public Integer remove(String k) { Entry e; Node p; int pos; /* ----------------------- Find k to remove ----------------------- */ e = keySearch(k); /* ======================= Check if k exists... ======================= */ if ( e == null ) { return(null); // Not found, nothing to delete... } /* ========================= We found the entry =============== */ Entry old = e; // Save for return value /* ---------------------------------------------------- Note: searchEndPos = node containing entry e ---------------------------------------------------- */ p = searchEndPos; /* ---------------------------------------------------- Find position of e inside p: p --> | T0 | e0 | T1 | e1 | T2 | e2 | T3 | Which of the e? is e ???? ---------------------------------------------------- */ for ( pos = 0; pos < 3; pos++ ) if ( p.e[pos] == e ) break; /* ---------------------------------------------------- Delete entry e from 2,4-tree ---------------------------------------------------- */ if ( p.child[0] == null /* ====> leaf node */ ) { /* ================================================== Delete entry e from a leaf node Note: this is done by MOVING right most entries into the slot containing e !!! ================================================== */ for ( int i = pos; i < 2; i++ ) p.e[i] = p.e[i+1]; p.e[2] = null; // The right most entry is for sure null ! } else { /* ------------------------------------------------ Non-leaf: 1. replace e with e's successor ------------------------------------------------ */ Node q; /* ------------------------------------------------ Use q to traverse to p's successor ------------------------------------------------ */ q = p.child[pos+1]; // Go right while ( q.child[0] != null ) q = q.child[0]; // Go left all the way down. /* ------------------------------------------------ Replace e by succ ------------------------------------------------ */ p.e[pos] = q.e[0]; // Replace e by e's successor /* ============================================== Delete succ from q (succ is always at pos 0) ============================================== */ for ( int i = 0; i < 2; i++ ) // Delete e's successor in q q.e[i] = q.e[i+1]; q.e[2] = null; /* ----------------------------------------------------- The target entry is now q ----------------------------------------------------- */ p = q; // node where entry was deleted } /* ********************************************************* Check for underflow situation in delete target location ********************************************************* */ if ( p.e[0] == null /* <===> node is empty */ ) { handleUnderflow(p, null); // Node p is empty // Second parameter is a subtree // hung under p } return old.value; // Return old value } |
/* ------------------------------------------------------- handleUnderflow(p, Z): handle the overflow condition in node p (p = empty !) Z = subtree that is left hanging when p became empty ------------------------------------------------------- */ void handleUnderflow(Node p, Node Z) { /* ---------------------------- Check if p is the root ---------------------------- */ if ( p == root ) { root = Z; // The subtree is the new tree !! return; // DONE ! } /* =================================================== Gather information before transfer or merge =================================================== */ Node parent; int pos; parent = p.parent; pos = index of subtree p in parent; // pos = 0, 1, 2 /* ------------------------------------------------------------- The meaning of pos: p's parent --> | C0 | e0 | C1 | e1 | C2 | e2 | C3 | ^ ^ ^ ^ | | | | if: C0==p C1==p C2==p C3==p then: pos=0 pos=1 pos=2 pos=3 -------------------------------------------------------------- */ /* ------------------------------------------------------------- Try: TRANSFER against your right sibling ------------------------------------------------------------- */ if ( p has a right sibling && p's right sibling has >= 2 entries ) { /* ------------------------------------- Transfer with right sibling See: click here ------------------------------------- */ R_Sibling = p's right sibling node; Perform this transfer: |
public void handleUnderflow(Node p, Node Z) { /* ================================================ Check if the root node is empty Note: This is a base case of the recursion.... ================================================ */ if ( p == root ) { root = Z; // Delete the empty root node p return; } /* =================================================== Gather information before transfer or merge =================================================== */ Node parent; int pos; parent = p.parent; for ( pos = 0; pos < 4; pos++ ) { if ( parent.child[pos] == p ) break; } /* ------------------------------------------------------------- The meaning of pos: p's parent --> | C0 | e0 | C1 | e1 | C2 | e2 | C3 | ^ ^ ^ ^ | | | | if: C0==p C1==p C2==p C3==p then: pos=0 pos=1 pos=2 pos=3 -------------------------------------------------------------- */ /* ------------------------------------------------------------- TRY a TRANSFER with your RIGHT sibling ------------------------------------------------------------- */ if ( (pos <= 2 && parent.child[pos+1] != null) /* p has a right sibling */ && parent.child[pos+1].e[1] != null /* R sibling has >= 2 entries */) { Node R_sibling = parent.child[pos+1]; |
How to run the program:
|
Sample output:
Legend: red denotes the next deleted entry darkred denotes the entries involved in op darkgreen shows when the involved entries end up 2:((m,6),(-),(-)) 3:((ka,6),(l,6),(-)) 1:((kb,6),(-),(-)) 0:((j,6),(k,6),(-)) 1:((h,8),(-),(-)) 2:((g,7),(-),(-)) 0:((e,5),(f,6),(-)) 0:((ba,6),(d,4),(i,9)) 1:((ca,6),(-),(-)) 1:((c,3),(-),(-)) 0:((bb,6),(-),(-)) 3:((b,2),(-),(-)) 2:((ae,6),(af,6),(-)) <--- will be replaced by (ae,6) 0:((ab,6),(ad,6),(ax,6)) 1:((ac,6),(-),(-)) 0:((a,1),(aa,6),(-)) ================================================================== == After remove(ad): 2:((m,6),(-),(-)) 3:((ka,6),(l,6),(-)) 1:((kb,6),(-),(-)) 0:((j,6),(k,6),(-)) 1:((h,8),(-),(-)) 2:((g,7),(-),(-)) 0:((e,5),(f,6),(-)) 0:((ba,6),(d,4),(i,9)) 1:((ca,6),(-),(-)) 1:((c,3),(-),(-)) 0:((bb,6),(-),(-)) 3:((b,2),(-),(-)) 2:((af,6),(-),(-)) 0:((ab,6),(ae,6),(ax,6)) 1:((ac,6),(-),(-)) 0:((a,1),(aa,6),(-)) <---- will TRANSFER with Left sibling ==================================================== Underflow !!!! ===> Transfer with L sibling ((a,1),(aa,6),(-)) == After remove(ac): 2:((m,6),(-),(-)) 3:((ka,6),(l,6),(-)) 1:((kb,6),(-),(-)) 0:((j,6),(k,6),(-)) 1:((h,8),(-),(-)) 2:((g,7),(-),(-)) 0:((e,5),(f,6),(-)) 0:((ba,6),(d,4),(i,9)) 1:((ca,6),(-),(-)) 1:((c,3),(-),(-)) 0:((bb,6),(-),(-)) 3:((b,2),(-),(-)) 2:((af,6),(-),(-)) <---- will MERGE with Left sibling 0:((aa,6),(ae,6),(ax,6)) 1:((ab,6),(-),(-)) 0:((a,1),(-),(-)) ======================================== Underflow !!!! ===> MERGE with L sibling ((af,6),(-),(-)) == After remove(b): 2:((m,6),(-),(-)) 3:((ka,6),(l,6),(-)) 1:((kb,6),(-),(-)) 0:((j,6),(k,6),(-)) 1:((h,8),(-),(-)) 2:((g,7),(-),(-)) 0:((e,5),(f,6),(-)) 0:((ba,6),(d,4),(i,9)) 1:((ca,6),(-),(-)) 1:((c,3),(-),(-)) 0:((bb,6),(-),(-)) 2:((af,6),(ax,6),(-)) 0:((aa,6),(ae,6),(-)) 1:((ab,6),(-),(-)) 0:((a,1),(-),(-)) |