Entry put(Key k, Value v) { /* ------------------------- Special case: empty tree ------------------------- */ if ( root == null ) { root = create new root node; root.entry[0] = (k,v); return(null); } /* ----------------------------------- Find key k in the tree.... ----------------------------------- */ p = keySearch(k); // keySearch: see click here /* ----------------------------------- Handle the case when key k is found ----------------------------------- */ if ( p != null && p.key == k ) { p.value = v; return (old Value in p); // Done... } /* ------------------------------------------------ Key k not found... Then: keySearch(k) ended at a node p with only external children node ------------------------------------------------ */ p = Node of (2,4)-tree where keySearch(k) ended; // This is a node with only external children node e = new Entry(k, v); // Create entry using k and v /* ------------------------------------------------ Invoke a recursive insert procedure (It MUST be recursive because we may need to REPEAT the procedure further up the tree !!! (That is when a node overflows and splits !!!) ------------------------------------------------ */ insertEntryInThisNode(e, null (the right subtree), p); // Parameters: // e = the new entry that is inserted // second parameter = the RIGHT subtree of e // p = node used to insert e } |
Java code:
/* ================================================ put(k): insert (k,v) ================================================ */ Integer put(String k, Integer v) { Node p; Entry e; /* ------------------------ Special case: empty tree ------------------------ */ if ( root == null ) { p = new Node(); p.e[0] = new Entry(k, v); root = p; p.parent = p; // Parent(root) == root return(null); } /* ------------------------ Other cases ------------------------ */ e = keySearch(k); // keySearch sets "searchEndPos = last node visited /* ------------------------ key found, update value ------------------------ */ if ( e != null && k.compareTo(e.key) == 0 ) { Integer oldValue; System.out.print("*** Update value of " + k + " in entry " + e); oldValue = e.value; e.value = v; return(oldValue); } /* ------------------------------------------ key not found: insert (k,v) in node "searchEndPos" ------------------------------------------ */ e = new Entry(k,v); insertEntryInThisNode(e, null, searchEndPos); return(null); } |
(Pseudo code):
/* ----------------------------------------------------------- insertEntryInThisNode( e, rightSubTree, p ): inserts: [e, e's rightSubTree] into node p Note: Since p contains multiple entries, we must insert e in the correct position inside p !!! This will require moving some elements in an array !! ------------------------------------------------------------ */ void insertEntryInThisNode(Entry e, Node rightSubTree, Node p) { /* ========================================= Make p the parent node of rightSubTree ========================================= */ if ( rightSubTree != null ) rightSubTree.parent = p; /* ---------------------------------------------- Case 1: node p has space left for new entry ---------------------------------------------- */ if ( # enties in p <= 2 ) { insert [e, rightSubTree] in the correct position inside p return; } /* ------------------------------------------------------------- Case 2: node p is full: 1. split the oevrflow node 2. insert left over entry + right tree into parent node ------------------------------------------------------------- */ Sort the full node + (e, rightSubTree): q = new Node(); // Create a new node to hold entries Split the entries and links as follows: Insert "left-over entry" and its right subtree in parent node: /* ---------------------------------------------- Handle p = root DIFFERENTLY!!! ---------------------------------------------- */ if ( p == root ) { /* --------------------------------------------------------------- Special case: SPLIT the root node (tree wil grow in height !!! --------------------------------------------------------------- */ newRoot = new Node(); newRoot = p n q; // p = left subtree, n = key, q = right subtree p.parent = newRoot; q.parent = newRoot; root = newRoot; // New root node Result: } else { insertEntryInThisNode(n, q, p.parent); } } |
/* ======================================================= insertEntryInThisNode(e, rightSubTree, p): 1. insert "e" and its "rightSubTree" in node p 2. if over flow, split and insert in parent ======================================================= */ public void insertEntryInThisNode(Entry e, Node rightSubTree, Node p) { int i; Node q; Entry n; /* ========================================= Make p the parent node of rightSubTree ========================================= */ if ( rightSubTree != null ) rightSubTree.parent = p; if ( p.e[2] == null ) { /* ----------------- There is space ----------------- */ insertEntryDirectlyInNode(e, rightSubTree, p); // Find the spot for (e, rightSubTree) inside p // We may need to move array elements !!! } else { /* ----------------------------------------------- There is no more space .... Split insert !!! ----------------------------------------------- */ /* ======================================= Make a virtual node with 4 keys (It's easier to sort in an array !!!) ======================================= */ Entry[] x_e = new Entry[4]; Node[] x_child = new Node[5]; /* ======================================================== Sort the keys e[0].key, e[1].key, e[2].key and k (and their subtrees) using the virtual node ======================================================== */ x_child[0] = p.child[0]; // left tree does not change position /* ================================== 1. Fill with keys < k ================================== */ i = 0; while ( i < 3 && p.e[i].key.compareTo(e.key) < 0 ) { x_e[i] = p.e[i]; x_child[i+1] = p.child[i+1]; i++; } /* ================================================= 2. Then fill it with e and it's RIGHT subtree ================================================== */ x_e[i] = e; x_child[i+1] = rightSubTree; /* ======================================================== 3. Finally, fill it with the rest (these keys are > k) ======================================================== */ while ( i < 3 ) { x_e[i+1] = p.e[i]; x_child[i+2] = p.child[i+1]; i++; } // After the above statements, we have the following situation: /* ===================================== Distribute keys into p and q p: first 2 keys q: last key ===================================== */ p.child[0] = x_child[0]; p.e[0] = x_e[0]; // first key p.child[1] = x_child[1]; p.e[1] = x_e[1]; // second key p.child[2] = x_child[2]; p.e[2] = null; p.child[3] = null; n = x_e[2]; // n = 3rd key q = new Node(); // Make a new node to hold the RIGHT half /* ============================================= Move entries and substrees into NEW node q ============================================= */ q.child[0] = x_child[3]; q.e[0] = x_e[3]; // Last key q.child[1] = x_child[4]; // We now have the following situation: /* ====================================================== Change the PARENT links of the subtrees c4 and c5 !!! ====================================================== */ if ( q.child[0] != null ) // I forgot these 2 if-statements and q.child[0].parent = q; // has a hard time finding this bug... if ( q.child[1] != null ) q.child[1].parent = q; /* ---------------------------------------------- Handle p = root DIFFERENTLY!!! ---------------------------------------------- */ if ( p == root ) { /* Split root */ Node newRoot = new Node(); newRoot.child[0] = p; newRoot.e[0] = n; newRoot.child[1] = q; p.parent = newRoot; q.parent = newRoot; root = newRoot; // Result: } else { // ************************* Recurse ! insertEntryInThisNode(n, q, p.parent); } } } |
How to run the program:
|
Sample output:
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Enter a key: a Enter a value: 1 put(a,1): == After put(a,1): 0:(a,(-),(-)) vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv cheung@home6(377)> java TestProg vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Enter a key: a Enter a value: 1 put(a,1): == After put(a,1): 0:((a,1),(-),(-)) vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Enter a key: b Enter a value: 2 put(b,2): Insert b in node ((a,1),(-),(-)) == After put(b,2): 0:((a,1),(b,2),(-)) vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Enter a key: b Enter a value: 4 put(b,4): *** Update value of b in entry (b,2) == After put(b,4): 0:((a,1),(b,4),(-)) vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Enter a key: c Enter a value: 7 put(c,7): Insert c in node ((a,1),(b,4),(-)) == After put(c,7): 0:((a,1),(b,4),(c,7)) vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Enter a key: d 4 Enter a value: put(d,4): *** Split insert ! ---- Transitional node = a b (c) d == After put(d,4): 1:((d,4),(-),(-)) 0:((c,7),(-),(-)) 0:((a,1),(b,4),(-)) |