|
| 1 | +import parse from '../../pseudocode/parse'; |
| 2 | + |
| 3 | + |
| 4 | +// XXX Best skip NullTable and HashInit function completely - just start |
| 5 | +// animation with initialised table |
| 6 | +// XXX: if dynamic tables are not implemented, both CheckTableFullness |
| 7 | +// and CheckTableFullness should be removed |
| 8 | +// NOTE: code now no longer explicitly keeps track of number of |
| 9 | +// insertions and CheckTableFullness is modified so some bookmarks (eg, |
| 10 | +// 4, 19, 20) no longer exist and controller code will have to change |
| 11 | +const main = ` |
| 12 | +
|
| 13 | + \\Code{ |
| 14 | + NullTable |
| 15 | + i <- 0 |
| 16 | + while i<TableSize \\B 2 |
| 17 | + \\In{ |
| 18 | + T[i] <- Empty // Table must start with all slots empty |
| 19 | + i <- i+1 |
| 20 | + \\In} |
| 21 | + \\Code} |
| 22 | +
|
| 23 | + \\Code{ |
| 24 | + Main |
| 25 | + HashInit(T) // TableSize is prime \\B 1 |
| 26 | + \\In{ |
| 27 | + Initialize Hash Table Slots to Empty \\Ref NullTable |
| 28 | + Insertions <- 0 // Keep track of how full table is \\B 3 |
| 29 | + \\In} |
| 30 | +
|
| 31 | + //======================================================= |
| 32 | +
|
| 33 | + HashInsert(T, k) // Insert key k into table |
| 34 | + \\In{ |
| 35 | + Check how full the table is \\Ref CheckTableFullness |
| 36 | + \\Expl{ One empty slot must always be maintained, to |
| 37 | +prevent infinite loops when searching. |
| 38 | + Performance also degrades with fewer empty slots and we may want to reconstruct the table with a larger size. |
| 39 | + See Background for more details. |
| 40 | + \\Expl} |
| 41 | + \\Note{The following has a choose increment value -- assumes we can make a choice |
| 42 | + here between linear probing and double hashing. NOTE TO DEVELOPERS: We are planning to |
| 43 | + make linear probing and double hashing as two separate modules. |
| 44 | + So -- in the linear probing pseudocode there is no "Choose increment", |
| 45 | + increment is just 1, and further on in the pseudocode Increment will be replaced |
| 46 | + by 1. For the double hashing, we need to calculate the second hash function. |
| 47 | + \\Note} |
| 48 | + i <- hash(k) \\Ref Hash1 |
| 49 | + Choose Increment value in case of collisions \\Ref SetIncrement |
| 50 | + Search for unoccupied slot \\Ref InsertionLoop |
| 51 | + \\Expl{ Check slots in steps of the chosen increment value, wrapping around at the end of the table |
| 52 | + \\Expl} |
| 53 | + T[i] <- k // unoccupied slot found so we put k in it \\B 9 |
| 54 | + // Done \\B 10 |
| 55 | + \\In} |
| 56 | + |
| 57 | + //======================================================= |
| 58 | +
|
| 59 | + HashDelete(T, k) // Delete key k in table T \\B 11 |
| 60 | + \\In{ |
| 61 | + Check how full the table is \\Ref CheckTableFullnessDel |
| 62 | + \\Expl{ If there are lots of Deleted slots we may want to reconstruct the table to improve performance. |
| 63 | + \\Expl} |
| 64 | + i <- hash(k) // Expand Hash in HashInsert for details \\B 16 |
| 65 | + Choose Increment value for stepping through T //Expand ChooseIncrement in HashInsert for details \\B 17 |
| 66 | + while not (T[i] = k or T[i] = Empty or T[i] = Deleted) // search for k or Empty or Deleted \\B 12 |
| 67 | + \\In{ |
| 68 | + i <- (i + Increment) mod TableSize \\B 13 |
| 69 | + \\Expl{ T[i] is not k or Empty so we jump ahead Increment |
| 70 | + steps and "wrapping around" if we reach the end, mirroring |
| 71 | + the insertion code. |
| 72 | + \\Expl} |
| 73 | + \\In} |
| 74 | + if T[i] = k \\B 14 |
| 75 | + \\In{ |
| 76 | + T[i] <- Deleted \\B 15 |
| 77 | + \\Expl{ If T[i] contains the index element, the slot is flagged as deleted. |
| 78 | + We display "X" to indicate Deleted slots. |
| 79 | + \\Expl} |
| 80 | + \\In} |
| 81 | + else \\B 18 |
| 82 | + \\In{ |
| 83 | + // Do nothing |
| 84 | + \\In} |
| 85 | + \\In} |
| 86 | + \\Code} |
| 87 | +
|
| 88 | + \\Code{ |
| 89 | + InsertionLoop |
| 90 | + \\Expl{ If T[i] = k then k already exists in the table. We could explicitly check |
| 91 | + for this but the code here simply over-writes the previous |
| 92 | + ocurrence of k, as if the slot was empty. |
| 93 | + \\Expl} |
| 94 | + while T[i] is occupied by another element // search for unoccupied slot \\B 7 |
| 95 | + \\Expl{ If T[i] = k then k already exists in the table. We could explicitly check |
| 96 | + for this but the code here simply over-writes the previous |
| 97 | + ocurrence of k, as if the slot was empty. |
| 98 | + \\Expl} |
| 99 | + \\In{ |
| 100 | + i <- (i + Increment) mod TableSize \\B 8 |
| 101 | + \\Expl{ T[i] is occupied so we jump ahead Increment steps. |
| 102 | + We use modulo TableSize to "wrap around" if we reach the end. |
| 103 | + \\Expl} |
| 104 | + \\In} |
| 105 | + \\Code} |
| 106 | +
|
| 107 | + \\Code{ |
| 108 | + CheckTableFullness |
| 109 | + if there are too few empty slots \\B 19 |
| 110 | + \\Expl{ A small number of empty slots leads to poor performance. |
| 111 | + To overcome this we must construct a new (possibly |
| 112 | + larger) table and insert all the elements of T. |
| 113 | + This also gets rid of deleted slots. |
| 114 | + \\Expl} |
| 115 | + \\In{ |
| 116 | + Create a new empty table T1 |
| 117 | + \\Expl{ Without deleted elements, it is best for |
| 118 | + the table size to approximately double. If there are many deleted slots |
| 119 | + it may be OK to keep the same size. |
| 120 | + \\Expl} |
| 121 | + Insert each key in T into T1 |
| 122 | + \\Note{ Animation can stop at this line for each |
| 123 | + key (could possibly have an extra level of expansion) |
| 124 | + \\Note} |
| 125 | + T <- T1 |
| 126 | + \\In} |
| 127 | + \\Code} |
| 128 | + \\Code{ |
| 129 | + CheckTableFullnessDel |
| 130 | + if there are too many deleted slots \\B 19 |
| 131 | + \\Expl{ A small number of empty slots leads to poor performance. |
| 132 | + To overcome this we must construct a new table, which gets rid of deleted slots. |
| 133 | + It may also be worthwhile to reduce the table size. |
| 134 | + \\Expl} |
| 135 | + \\In{ |
| 136 | + Create a new empty table T1 |
| 137 | + \\Expl{ If there are many deleted slots |
| 138 | + it may be be worthwhile to reduce the table size. |
| 139 | + \\Expl} |
| 140 | + Insert each key in T into T1 |
| 141 | + \\Expl{ For better performance we could omit inserting k here and skip the later HashDelete code. |
| 142 | + \\Expl} |
| 143 | + \\Note{ Animation can stop at this line for each |
| 144 | + key (could possibly have an extra level of expansion) |
| 145 | + \\Note} |
| 146 | + T <- T1 |
| 147 | + \\In} |
| 148 | + \\Code} |
| 149 | +` |
| 150 | +export const hash1 = ` |
| 151 | + \\Code{ |
| 152 | + Hash1 |
| 153 | + i <- (k * BIGPRIME) mod TableSize \\B 5 |
| 154 | + \\Expl{ BIGPRIME much bigger than TableSize (which is also prime). |
| 155 | + The object is to spread the values across the hash table as widely as possible. |
| 156 | + Here we use BIGPRIME = 3457 |
| 157 | + \\Expl} |
| 158 | + \\Code} |
| 159 | +` |
| 160 | +export const linearProbingIncrement = ` |
| 161 | +
|
| 162 | + \\Code{ |
| 163 | + SetIncrement |
| 164 | + Increment <- 1 \\B 6 |
| 165 | + \\Expl{ For linear probing, if we have a collision we successively look at the |
| 166 | + next table entry. |
| 167 | + \\Expl} |
| 168 | + \\Code} |
| 169 | +
|
| 170 | +` |
| 171 | + |
| 172 | +export const doubleHashingIncrement = ` |
| 173 | +
|
| 174 | + \\Code{ |
| 175 | + SetIncrement |
| 176 | + Increment <- (k * BIGPRIME2) mod SMALLISHPRIME + 1 \\B 6 |
| 177 | + \\Expl{Double hashing resolves collisions by hashing the key k a second time to set the increment |
| 178 | + to find the next empty slot in the table R. The value given by the function must be non-zero |
| 179 | + and must also be relatively prime to the table size. |
| 180 | + Here BIGPRIME2 is 1429 and SMALLISHPRIME is 3 or 23, depending on the table size selected. |
| 181 | + \\Expl} |
| 182 | + \\Code} |
| 183 | +` |
| 184 | +export const doubleHashing = parse(main + hash1 + '\n' + doubleHashingIncrement); |
| 185 | +export const linearProbing = parse(main + hash1 + '\n' + linearProbingIncrement); |
0 commit comments