|
| 1 | +import Node from './Node'; |
| 2 | + |
| 3 | +import list_debug from './list_debug'; |
| 4 | +import list_insert from './list_insert'; |
| 5 | +import list_remove from './list_remove'; |
| 6 | +import list_concatenate from './list_concatenate'; |
| 7 | +import list_reset_parent from './list_reset_parent'; |
| 8 | + |
| 9 | +import consolidate from './consolidate'; |
| 10 | +import cut from './cut'; |
| 11 | +import cascading_cut from './cascading_cut'; |
| 12 | + |
| 13 | +/** |
| 14 | + * See CLRS09 Chapter 19 on the Fibonacci Heap. |
| 15 | + */ |
| 16 | +export default class FibonacciHeap { |
| 17 | + /** |
| 18 | + * Make-heap: creates a new empty heap. |
| 19 | + * |
| 20 | + * To make an empty Fibonacci heap, the MAKE-FIB-HEAP procedure |
| 21 | + * allocates and returns the Fibonacci heap object H, where H.n = 0 and |
| 22 | + * H.min = NIL ; there are no trees in H. Because t(H) = 0 and m(H) = 0, |
| 23 | + * the potential of the empty Fibonacci heap is pot(H) = 0. The amortized |
| 24 | + * cost of MAKE-FIB-HEAP is thus equal to its O(1) actual cost. |
| 25 | + */ |
| 26 | + constructor(compare) { |
| 27 | + //console.debug('constructor'); |
| 28 | + this.compare = compare; // Comparison function |
| 29 | + /** |
| 30 | + * We access a given Fibonacci heap H by a pointer H.min to the root of |
| 31 | + * a tree containing the minimum key; we call this node the minimum |
| 32 | + * node of the Fibonacci heap. If more than one root has a key with the |
| 33 | + * minimum value, then any such root may serve as the minimum node. |
| 34 | + * When a Fibonacci heap H is empty, H.min is NIL. |
| 35 | + */ |
| 36 | + this.min = null; |
| 37 | + } |
| 38 | + |
| 39 | + /** |
| 40 | + * Find-min: simply return the top element of the heap. |
| 41 | + */ |
| 42 | + head() { |
| 43 | + //console.debug('head'); |
| 44 | + if (this.min === null) return undefined; |
| 45 | + return this.min.value; |
| 46 | + } |
| 47 | + |
| 48 | + /** |
| 49 | + * Minimum: return a pointer to the element whose key is minimum |
| 50 | + * |
| 51 | + * The minimum node of a Fibonacci heap H is given by the pointer H.min, so |
| 52 | + * we can find the minimum node in O(1) actual time. Because the potential |
| 53 | + * of H does not change, the amortized cost of this operation is equal to |
| 54 | + * its O(1) actual cost. |
| 55 | + */ |
| 56 | + headreference() { |
| 57 | + //console.debug('headreference'); |
| 58 | + return this.min; |
| 59 | + } |
| 60 | + |
| 61 | + /** |
| 62 | + */ |
| 63 | + pop() { |
| 64 | + //console.debug('pop'); |
| 65 | + const min = this.popreference(); |
| 66 | + return min === null ? undefined : min.value; |
| 67 | + } |
| 68 | + |
| 69 | + /** |
| 70 | + * FIB-HEAP-EXTRACT-MIN(H): |
| 71 | + * |
| 72 | + * Extracting the minimum node |
| 73 | + * |
| 74 | + * The process of extracting the minimum node is the most complicated of |
| 75 | + * the operations presented in this section. It is also where the delayed |
| 76 | + * work of consolidating trees in the root list finally occurs. The |
| 77 | + * following pseudocode extracts the minimum node. The code assumes for |
| 78 | + * convenience that when a node is removed from a linked list, pointers |
| 79 | + * remaining in the list are updated, but pointers in the extracted node |
| 80 | + * are left unchanged. It also calls the auxiliary procedure CONSOLIDATE, |
| 81 | + * which we shall see shortly. |
| 82 | + */ |
| 83 | + popreference() { |
| 84 | + //console.debug('popreference'); |
| 85 | + const z = this.min; |
| 86 | + if (z === null) return null; |
| 87 | + //console.debug('z is', z.value); |
| 88 | + list_debug(z.next, 'z list'); |
| 89 | + if (z.children !== null) { |
| 90 | + list_debug(z.children, 'z children'); |
| 91 | + list_reset_parent(z.children); |
| 92 | + list_concatenate(z, z.children); |
| 93 | + list_debug(z.next, 'z after concatenation with its children'); |
| 94 | + } |
| 95 | + |
| 96 | + if (z === z.next) this.min = null; |
| 97 | + else { |
| 98 | + list_remove(z); |
| 99 | + list_debug(z.next, 'z after removal'); |
| 100 | + this.min = consolidate(this.compare, z.next); |
| 101 | + } |
| 102 | + |
| 103 | + z.next = z; // TODO remove this |
| 104 | + z.prev = z; |
| 105 | + z.children = null; |
| 106 | + z.degree = 0; |
| 107 | + z.mark = false; |
| 108 | + //console.debug('popped', z.value); |
| 109 | + return z; |
| 110 | + } |
| 111 | + |
| 112 | + /** |
| 113 | + * Create a new node for the inserted element and put it into the heap. |
| 114 | + */ |
| 115 | + push(value) { |
| 116 | + //console.debug('push', value); |
| 117 | + const node = new Node(value); |
| 118 | + return this.pushreference(node); |
| 119 | + } |
| 120 | + |
| 121 | + /** |
| 122 | + * Insert: insert new node. |
| 123 | + * /!\ ref.next = ref.prev = ref which means all references that are |
| 124 | + * external to the tree must reset .next and .prev and one must not call |
| 125 | + * FibonacciHeap#pushreference with an internal reference from this tree or |
| 126 | + * another, except the root of another tree. |
| 127 | + * |
| 128 | + * Change in potential is 1. Therefore amortized cost is O(1). |
| 129 | + */ |
| 130 | + pushreference(ref) { |
| 131 | + //console.debug('pushreference'); |
| 132 | + if (this.min === null) { |
| 133 | + // Create a root list for H containing just x (by precondition) |
| 134 | + this.min = ref; |
| 135 | + } else { |
| 136 | + // This.min != null != ref |
| 137 | + // Insert x into H's root list |
| 138 | + list_insert(this.min, ref); |
| 139 | + // Update minimum |
| 140 | + if (this.compare(ref.value, this.min.value) < 0) { |
| 141 | + this.min = ref; |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + return ref; |
| 146 | + } |
| 147 | + |
| 148 | + /** |
| 149 | + * Union: Uniting two Fibonacci heaps |
| 150 | + * |
| 151 | + * The following procedure unites Fibonacci heaps H_1 and H_2, destroying |
| 152 | + * H_1 and H_2 in the process. It simply concatenates the root lists of H_1 |
| 153 | + * and H_2 and then determines the new minimum node. Afterward, the objects |
| 154 | + * representing H_1 and H_2 will never be used again. |
| 155 | + * |
| 156 | + * /!\ Assumes the same comparison function is used in both trees. |
| 157 | + * |
| 158 | + * Change in potential is zero. Amortized cost is O(1), the actual cost. |
| 159 | + */ |
| 160 | + meld(other) { |
| 161 | + //console.debug('meld'); |
| 162 | + const ref = other.min; |
| 163 | + if (ref === null) return; |
| 164 | + if (this.min === null) this.min = ref; |
| 165 | + else { |
| 166 | + // This.min != null != ref |
| 167 | + // Concatenate the root list of H_2 with the root list of H |
| 168 | + list_concatenate(this.min, ref); |
| 169 | + // Update minimum |
| 170 | + if (this.compare(ref.value, this.min.value) < 0) { |
| 171 | + this.min = ref; |
| 172 | + } |
| 173 | + } |
| 174 | + } |
| 175 | + |
| 176 | + /** |
| 177 | + * Synonym for FibonacciHeap#meld. |
| 178 | + * TODO Remove this. |
| 179 | + */ |
| 180 | + merge(other) { |
| 181 | + this.meld(other); |
| 182 | + } |
| 183 | + |
| 184 | + /** |
| 185 | + * @param {Node} ref Non-null internal node object. |
| 186 | + * @param {Object} value The new value for ref. |
| 187 | + */ |
| 188 | + update(ref, value) { |
| 189 | + //console.debug('update'); |
| 190 | + const d = this.compare(value, ref.value); |
| 191 | + |
| 192 | + if (d < 0) this.decreasekey(ref, value); |
| 193 | + else if (d > 0) this.increasekey(ref, value); |
| 194 | + else ref.value = value; |
| 195 | + } |
| 196 | + |
| 197 | + /** |
| 198 | + * FIB-HEAP-DECREASE-kEY: |
| 199 | + * @param {Node} ref Non-null internal node object. |
| 200 | + * @param {Object} value The new value for ref. |
| 201 | + */ |
| 202 | + decreasekey(ref, value) { |
| 203 | + //console.debug('decreasekey'); |
| 204 | + ref.value = value; |
| 205 | + if (ref !== this.min) { |
| 206 | + // This.min != null, ref != null |
| 207 | + const y = ref.parent; |
| 208 | + if (y !== null && this.compare(ref.value, y.value) < 0) { |
| 209 | + cut(ref, y); |
| 210 | + list_insert(this.min, ref); |
| 211 | + for (const x of cascading_cut(y)) list_insert(this.min, x); |
| 212 | + } |
| 213 | + |
| 214 | + if (this.compare(ref.value, this.min.value) < 0) { |
| 215 | + this.min = ref; |
| 216 | + } |
| 217 | + } |
| 218 | + } |
| 219 | + |
| 220 | + /** |
| 221 | + * Increase-key: remove the item at the key to be increased, replace |
| 222 | + * the key with a larger key, then push the result back into the heap. |
| 223 | + * |
| 224 | + * @param {Node} ref Non-null internal node object. |
| 225 | + * @param {Object} value The new value for ref. |
| 226 | + * |
| 227 | + */ |
| 228 | + increasekey(ref, value) { |
| 229 | + //console.debug('increasekey'); |
| 230 | + this.delete(ref); |
| 231 | + |
| 232 | + ref.value = value; |
| 233 | + |
| 234 | + this.pushreference(ref); |
| 235 | + } |
| 236 | + |
| 237 | + /** |
| 238 | + * Delete |
| 239 | + * ref must be internal |
| 240 | + * ref.prev and ref.next get reset to null |
| 241 | + */ |
| 242 | + delete(ref) { |
| 243 | + //console.debug('>>>>') |
| 244 | + //console.debug('DELETE', ref.value); |
| 245 | + if (ref !== this.min) { |
| 246 | + // This.min != null, ref != null |
| 247 | + const y = ref.parent; |
| 248 | + if (y !== null) { |
| 249 | + cut(ref, y); |
| 250 | + list_insert(this.min, ref); |
| 251 | + for (const x of cascading_cut(y)) list_insert(this.min, x); |
| 252 | + } |
| 253 | + |
| 254 | + this.min = ref; |
| 255 | + } |
| 256 | + |
| 257 | + this.popreference(); |
| 258 | + } |
| 259 | +} |
0 commit comments