|
Entry remove(Key k)
{
e = keySearch(k, root); // Find entry containing key k
// starting with the root node
if ( e == null )
return; // Not found, exit...
Delete the entry e from the node that contains e.
}
|
|
|
|
|
|
|
Note:
|
Example:
|
|
|
|
|
(This is exactly like a delete operation in a binary search tree)
|
|
|
|
|
|
|
|
|
|
|
|
Transfering an entry form the sibling node will cause an underflow at the sibling node itself !
|
|
Result:
|
|
|
|
|
|
|
|
|
|
Entry 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...
}
/* ------------------------------
Phase 1: remove entry e
------------------------------ */
p = node containing e;
if ( p has an internal child node )
{
/* ----------------------------
Replace e with e's successor
---------------------------- */
succ = successor entry of the deleted entry e;
Swap p.e with succ entry;
/* -----------------------------------------------------
The target entry is now in the successor's location
----------------------------------------------------- */
e = succ;
p = node containing succ entry;
}
delete e from p and reshuffle entries in p; // Delete entry
// Also: remember the old value
/* ***********************************************
Handle a possible underflow situation
*********************************************** */
if ( p has ≤ 1 entry )
{
handleUnderflow(p); // This is a recursive routine !
}
return the old value in e; // No underflow; done
}
|
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...
}
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 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: replace e with e's successor
------------------------------------------------ */
Node q;
/* ------------------------------------------------
Use q to traverse to p's successor
------------------------------------------------ */
System.out.println("pos = " + pos);
q = p.child[pos+1]; // Go right
while ( q.child[0] != null )
q = q.child[0]; // Go left all the way down.
/* ------------------------------------------------
Delete e
------------------------------------------------ */
System.out.println("Replace " + p.e[pos] + " with: " + q.e[0]);
p.e[pos] = q.e[0]; // Replace e by e's successor
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 the successor's location
----------------------------------------------------- */
p = q; // node where entry was deleted
System.out.println("Result: p = " + p);
}
/* ***********************************************
Check for a possible underflow situation
*********************************************** */
if ( p.e[0] == null )
{
handleUnderflow(p, null); // Node p is empty
// Second parameter is a subtree
// hung under p
}
return old.value; // Return old value
}
|
/* -------------------------------------------------------
handleUnderflow(p): handle the overflow condition in
node p (p = empty !)
Node format:
p: {p1, k1, p2, k2, p3, k3, p4}
k1, k2, k3 are keys (with values)
p1, p2, p3, p4 are subtree pointers
Node p (input) is empty - i.e., it has NO keys
BUT: p.p1 points to the subtree of the underflow node
------------------------------------------------------- */
void handleUnderflow(Node p)
{
/* ----------------------------
Check if p is the root
---------------------------- */
if ( p == root )
{
root = p.p1; // New root...
return; // DONE !
}
/* -------------------------------------------------------------
Solve underflow with a TRANSFER against your right sibling
------------------------------------------------------------- */
if ( p.right_sibling != null && p.right_sibling has >= 2 entries )
{
/* -------------------------------------
Transfer p1 and k1 from right sibling
See: click here
------------------------------------- */
s = p.right_sibling;
pa = p.parent;
i = index of subtree p in parent pa; // i = 1, 2 or 3
/* --------------------------------------------------
Example:
if i == 1, then:
pa: | p1 | k1 | p2 | k2 | p3 | k3 | p4 |
/ \
p s
(s is right sibling of p)
--------------------------------------------------- */
|
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;
}
/* -------------------------------------------------------------
Find the position of p inside p's parent node
p's parent --> | T0 | e0 | T1 | e1 | T2 | e2 | T3 |
^ ^ ^ ^
| | | |
pos=0 pos=1 pos=2 pos=3
Which of the T? is p ???? (pos = 0, 1, 2, or 3)
---------------------------------------------------- */
Node parent;
int pos;
parent = p.parent;
for ( pos = 0; pos < 4; pos++ )
{
if ( parent.child[pos] == p )
break;
}
/* -------------------------------------------------------------
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 */)
{
/* -----------------------------------------------------------
Legend to understand the transfer operation: (assume pos=1)
pos pos+1
| |
V V
parent --> | T0 | e0 | T1 | *e1* | T2 | e2 | T3 |
| |
p R_sibling
Entry sandwiched between pos and pos+1 is moved into p !!!
----------------------------------------------------------- */
Node R_sibling = parent.child[pos+1];
System.out.println("Underflow !!!! ===> Transfer with R sibling "
+ R_sibling);
p.child[0] = Z; // Hang Z
if ( Z != null ) Z.parent = p;
p.e[0] = parent.e[pos]; // Transfer parent's entry
p.child[1] = R_sibling.child[0]; // Transfer sibling's subtree
if ( p.child[1] != null )
p.child[1].parent = p;
parent.e[pos] = R_sibling.e[0]; // Move sibling entry to parent
/* ===================================
Delete e[0] in R_sibling
=================================== */
for ( int i = 0; i < 2; i++ ) // ... accomplish by shifting
{
R_sibling.e[i] = R_sibling.e[i+1];
R_sibling.child[i] = R_sibling.child[i+1];
}
R_sibling.e[2] = null; // e[2] is now empty
R_sibling.child[3] = null; // child[3] is now empty
return; // Done
}
/* -------------------------------------------------------------
TRY a TRANSFER with your LEFT sibling
------------------------------------------------------------- */
else if ( pos > 0 /* p has a left sibling */
&& parent.child[pos-1].e[1] != null /* L sibling has >= 2 entries */)
{
/* -----------------------------------------------------------
Legend to understand the transfer operation: (assume pos=1)
pos-1 pos
| |
V V
parent --> | T0 | *e0* | T1 | e1 | T2 | e2 | T3 |
| |
L_sibling p
Entry sandwiched between pos-1 and pos is moved into p !!!
----------------------------------------------------------- */
Node L_sibling = parent.child[pos-1];
int last;
System.out.println("Underflow !!!! ===> Transfer with L sibling "
+ L_sibling);
/* -------------------------------------
Find the last entry in L_sibling
------------------------------------- */
for ( last = 0; last < 3; last++ )
if ( L_sibling.e[last] == null )
break;
if ( last >= 3 || L_sibling.e[last] == null )
last--;
p.child[0] = L_sibling.child[last+1]; // Transfer sibling's subtree
if ( p.child[0] != null )
p.child[0].parent = p;
p.e[0] = parent.e[pos-1]; // Transfer parent's entry
p.child[1] = Z; // Hang Z
if ( Z != null ) Z.parent = p;
parent.e[pos-1] = L_sibling.e[last]; // Move sibling entry to parent
/* ===================================
Delete e[lst] in L_sibling
=================================== */
L_sibling.e[last] = null;
L_sibling.child[last+1] = null;
return; // Done
}
/* ========================================================
We MUST use a merge operation
Legend of the position of p inside p's parent node
p's parent --> | T0 | e0 | T1 | e1 | T2 | e2 | T3 |
^ ^ ^ ^
| | | |
pos=0 pos=1 pos=2 pos=3
======================================================== */
/* -------------------------------------------------------------
TRY a MERGE with your RIGHT sibling
------------------------------------------------------------- */
else if ( pos != 3 /* No Right sibling possible */
&& parent.child[pos+1] != null /* There is a right sibling */)
{
/* -----------------------------------------------------------
Legend to understand the merge operation: (assume pos=1)
pos pos+1
| |
V V
parent --> | T0 | e0 | T1 | *e1* | T2 | e2 | T3 |
| |
p R_sibling
Entry sandwiched between pos and pos+1 is moved into p !!!
----------------------------------------------------------- */
Node R_sibling = parent.child[pos+1]; // R_sibling has ONLY 1 entry !
System.out.println("Underflow !!!! ===> MERGE with R sibling "
+ R_sibling);
p.child[0] = Z; // Hang Z
if ( Z != null ) Z.parent = p;
p.e[0] = parent.e[pos]; // Transfer parent's entry
p.child[1] = R_sibling.child[0]; // Transfer sibling's subtree
if ( p.child[1] != null )
p.child[1] .parent = p;
p.e[1] = R_sibling.e[0]; // Transfer sibling (ONLY) entry
p.child[2] = R_sibling.child[1]; // Transfer sibling's subtree
if ( p.child[2] != null )
p.child[2] .parent = p;
/* ======================================
Delete parent.e[pos] in parent node
====================================== */
for ( int i = pos; i < 2; i++ )
{
parent.e[i] = parent.e[i+1];
parent.child[i+1] = parent.child[i+2];
}
parent.e[2] = null;
parent.child[3] = null;
if ( parent.e[0] == null )
handleUnderflow(parent, p);
}
else // pos == 3, we must merge LEFT ...
{
/* -----------------------------------------------------------
Legend to understand the transfer operation: (assume pos=1)
pos-1 pos
| |
V V
parent --> | T0 | *e0* | T1 | e1 | T2 | e2 | T3 |
| |
L_sibling p
Entry sandwiched between pos-1 and pos is moved into p !!!
----------------------------------------------------------- */
Node L_sibling = parent.child[pos-1]; // L_sibling has 1 entry !
System.out.println("Underflow !!!! ===> MERGE with L sibling "
+ L_sibling);
L_sibling.e[1] = parent.e[pos-1]; // Transfer parent's entry
L_sibling.child[2] = Z; // Hang Z
if ( Z != null ) Z.parent = p;
/* ======================================
Delete parent.e[pos] in parent node
====================================== */
for ( int i = pos-1; i < 2; i++ )
{
parent.e[i] = parent.e[i+1];
parent.child[i+1] = parent.child[i+2];
}
parent.e[2] = null;
parent.child[3] = null;
if ( parent.e[0] == null )
handleUnderflow(parent, L_sibling);
}
}
|
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),(-),(-))
|