data-structure-typed 1.36.7 → 1.36.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ import {DFSOrderPattern} from '../../types';
10
10
 
11
11
  export class Heap<E> {
12
12
  protected nodes: E[] = [];
13
- private readonly comparator: Comparator<E>;
13
+ protected readonly comparator: Comparator<E>;
14
14
 
15
15
  constructor(comparator: Comparator<E>) {
16
16
  this.comparator = comparator;
@@ -18,21 +18,29 @@ export class Heap<E> {
18
18
 
19
19
  /**
20
20
  * Insert an element into the heap and maintain the heap properties.
21
- * @param value - The element to be inserted.
21
+ * @param element - The element to be inserted.
22
22
  */
23
- add(value: E): Heap<E> {
24
- this.nodes.push(value);
23
+ add(element: E): Heap<E> {
24
+ return this.push(element);
25
+ }
26
+
27
+ /**
28
+ * Insert an element into the heap and maintain the heap properties.
29
+ * @param element - The element to be inserted.
30
+ */
31
+ push(element: E): Heap<E> {
32
+ this.nodes.push(element);
25
33
  this.bubbleUp(this.nodes.length - 1);
26
34
  return this;
27
35
  }
28
36
 
29
37
  /**
30
38
  * Remove and return the top element (smallest or largest element) from the heap.
31
- * @returns The top element or null if the heap is empty.
39
+ * @returns The top element or undefined if the heap is empty.
32
40
  */
33
- poll(): E | null {
41
+ poll(): E | undefined {
34
42
  if (this.nodes.length === 0) {
35
- return null;
43
+ return undefined;
36
44
  }
37
45
  if (this.nodes.length === 1) {
38
46
  return this.nodes.pop() as E;
@@ -44,6 +52,14 @@ export class Heap<E> {
44
52
  return topValue;
45
53
  }
46
54
 
55
+ /**
56
+ * Remove and return the top element (smallest or largest element) from the heap.
57
+ * @returns The top element or undefined if the heap is empty.
58
+ */
59
+ pop(): E | undefined {
60
+ return this.poll();
61
+ }
62
+
47
63
  /**
48
64
  * Float operation to maintain heap properties after adding an element.
49
65
  * @param index - The index of the newly added element.
@@ -97,11 +113,11 @@ export class Heap<E> {
97
113
 
98
114
  /**
99
115
  * Peek at the top element of the heap without removing it.
100
- * @returns The top element or null if the heap is empty.
116
+ * @returns The top element or undefined if the heap is empty.
101
117
  */
102
- peek(): E | null {
118
+ peek(): E | undefined {
103
119
  if (this.nodes.length === 0) {
104
- return null;
120
+ return undefined;
105
121
  }
106
122
  return this.nodes[0];
107
123
  }
@@ -115,10 +131,10 @@ export class Heap<E> {
115
131
 
116
132
  /**
117
133
  * Get the last element in the heap, which is not necessarily a leaf node.
118
- * @returns The last element or null if the heap is empty.
134
+ * @returns The last element or undefined if the heap is empty.
119
135
  */
120
- get leaf(): E | null {
121
- return this.nodes[this.size - 1] ?? null;
136
+ get leaf(): E | undefined {
137
+ return this.nodes[this.size - 1] ?? undefined;
122
138
  }
123
139
 
124
140
  /**
@@ -147,11 +163,11 @@ export class Heap<E> {
147
163
 
148
164
  /**
149
165
  * Use a comparison function to check whether a binary heap contains a specific element.
150
- * @param value - the element to check.
166
+ * @param element - the element to check.
151
167
  * @returns Returns true if the specified element is contained; otherwise, returns false.
152
168
  */
153
- has(value: E): boolean {
154
- return this.nodes.includes(value);
169
+ has(element: E): boolean {
170
+ return this.nodes.includes(element);
155
171
  }
156
172
 
157
173
  /**
@@ -235,3 +251,308 @@ export class Heap<E> {
235
251
  return binaryHeap;
236
252
  }
237
253
  }
254
+
255
+ export class FibonacciHeapNode<E> {
256
+ element: E;
257
+ degree: number;
258
+ left?: FibonacciHeapNode<E>;
259
+ right?: FibonacciHeapNode<E>;
260
+ child?: FibonacciHeapNode<E>;
261
+ parent?: FibonacciHeapNode<E>;
262
+ marked: boolean;
263
+ constructor(element: E, degree = 0) {
264
+ this.element = element;
265
+ this.degree = degree;
266
+ this.marked = false;
267
+ }
268
+ }
269
+
270
+ export class FibonacciHeap<E> {
271
+ root?: FibonacciHeapNode<E>;
272
+ protected min?: FibonacciHeapNode<E>;
273
+ size: number = 0;
274
+ protected readonly comparator: Comparator<E>;
275
+
276
+ constructor(comparator?: Comparator<E>) {
277
+ this.clear();
278
+ this.comparator = comparator || this.defaultComparator;
279
+
280
+ if (typeof this.comparator !== 'function') {
281
+ throw new Error('FibonacciHeap constructor: given comparator should be a function.');
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Default comparator function used by the heap.
287
+ * @param {E} a
288
+ * @param {E} b
289
+ * @protected
290
+ */
291
+ protected defaultComparator(a: E, b: E): number {
292
+ if (a < b) return -1;
293
+ if (a > b) return 1;
294
+ return 0;
295
+ }
296
+
297
+ /**
298
+ * Get the size (number of elements) of the heap.
299
+ * @returns {number} The size of the heap. Returns 0 if the heap is empty. Returns -1 if the heap is invalid.
300
+ */
301
+ clear(): void {
302
+ this.root = undefined;
303
+ this.min = undefined;
304
+ this.size = 0;
305
+ }
306
+
307
+ /**
308
+ * Create a new node.
309
+ * @param element
310
+ * @protected
311
+ */
312
+ protected createNode(element: E): FibonacciHeapNode<E> {
313
+ return new FibonacciHeapNode<E>(element);
314
+ }
315
+
316
+ /**
317
+ * Merge the given node with the root list.
318
+ * @param node - The node to be merged.
319
+ */
320
+ protected mergeWithRoot(node: FibonacciHeapNode<E>): void {
321
+ if (!this.root) {
322
+ this.root = node;
323
+ } else {
324
+ node.right = this.root.right;
325
+ node.left = this.root;
326
+ this.root.right!.left = node;
327
+ this.root.right = node;
328
+ }
329
+ }
330
+
331
+ /**
332
+ * O(1) time operation.
333
+ * Insert an element into the heap and maintain the heap properties.
334
+ * @param element
335
+ * @returns {FibonacciHeap<E>} FibonacciHeap<E> - The heap itself.
336
+ */
337
+ add(element: E): FibonacciHeap<E> {
338
+ return this.push(element);
339
+ }
340
+
341
+ /**
342
+ * O(1) time operation.
343
+ * Insert an element into the heap and maintain the heap properties.
344
+ * @param element
345
+ * @returns {FibonacciHeap<E>} FibonacciHeap<E> - The heap itself.
346
+ */
347
+ push(element: E): FibonacciHeap<E> {
348
+ const node = this.createNode(element);
349
+ node.left = node;
350
+ node.right = node;
351
+ this.mergeWithRoot(node);
352
+
353
+ if (!this.min || this.comparator(node.element, this.min.element) <= 0) {
354
+ this.min = node;
355
+ }
356
+
357
+ this.size++;
358
+ return this;
359
+ }
360
+
361
+ /**
362
+ * O(1) time operation.
363
+ * Peek at the top element of the heap without removing it.
364
+ * @returns The top element or undefined if the heap is empty.
365
+ * @protected
366
+ */
367
+ peek(): E | undefined {
368
+ return this.min ? this.min.element : undefined;
369
+ }
370
+
371
+ /**
372
+ * O(1) time operation.
373
+ * Get the size (number of elements) of the heap.
374
+ * @param {FibonacciHeapNode<E>} head - The head of the linked list.
375
+ * @protected
376
+ * @returns FibonacciHeapNode<E>[] - An array containing the nodes of the linked list.
377
+ */
378
+ consumeLinkedList(head?: FibonacciHeapNode<E>): FibonacciHeapNode<E>[] {
379
+ const nodes: FibonacciHeapNode<E>[] = [];
380
+ if (!head) return nodes;
381
+
382
+ let node: FibonacciHeapNode<E> | undefined = head;
383
+ let flag = false;
384
+
385
+ while (true) {
386
+ if (node === head && flag) break;
387
+ else if (node === head) flag = true;
388
+
389
+ if (node) {
390
+ nodes.push(node);
391
+ node = node.right;
392
+ }
393
+ }
394
+
395
+ return nodes;
396
+ }
397
+
398
+ /**
399
+ * O(log n) time operation.
400
+ * Remove and return the top element (smallest or largest element) from the heap.
401
+ * @param node - The node to be removed.
402
+ * @protected
403
+ */
404
+ protected removeFromRoot(node: FibonacciHeapNode<E>): void {
405
+ if (this.root === node) this.root = node.right;
406
+ if (node.left) node.left.right = node.right;
407
+ if (node.right) node.right.left = node.left;
408
+ }
409
+
410
+ /**
411
+ * O(log n) time operation.
412
+ * Remove and return the top element (smallest or largest element) from the heap.
413
+ * @param parent
414
+ * @param node
415
+ */
416
+ mergeWithChild(parent: FibonacciHeapNode<E>, node: FibonacciHeapNode<E>): void {
417
+ if (!parent.child) {
418
+ parent.child = node;
419
+ } else {
420
+ node.right = parent.child.right;
421
+ node.left = parent.child;
422
+ parent.child.right!.left = node;
423
+ parent.child.right = node;
424
+ }
425
+ }
426
+
427
+ /**
428
+ * O(log n) time operation.
429
+ * Remove and return the top element (smallest or largest element) from the heap.
430
+ * @param y
431
+ * @param x
432
+ * @protected
433
+ */
434
+ protected link(y: FibonacciHeapNode<E>, x: FibonacciHeapNode<E>): void {
435
+ this.removeFromRoot(y);
436
+ y.left = y;
437
+ y.right = y;
438
+ this.mergeWithChild(x, y);
439
+ x.degree++;
440
+ y.parent = x;
441
+ }
442
+
443
+ /**
444
+ * O(log n) time operation.
445
+ * Remove and return the top element (smallest or largest element) from the heap.
446
+ * @protected
447
+ */
448
+ protected consolidate(): void {
449
+ const A: (FibonacciHeapNode<E> | undefined)[] = new Array(this.size);
450
+ const nodes = this.consumeLinkedList(this.root);
451
+ let x: FibonacciHeapNode<E> | undefined, y: FibonacciHeapNode<E> | undefined, d: number, t: FibonacciHeapNode<E> | undefined;
452
+
453
+ for (const node of nodes) {
454
+ x = node;
455
+ d = x.degree;
456
+
457
+ while (A[d]) {
458
+ y = A[d] as FibonacciHeapNode<E>;
459
+
460
+ if (this.comparator(x.element, y.element) > 0) {
461
+ t = x;
462
+ x = y;
463
+ y = t;
464
+ }
465
+
466
+ this.link(y, x);
467
+ A[d] = undefined;
468
+ d++;
469
+ }
470
+
471
+ A[d] = x;
472
+ }
473
+
474
+ for (let i = 0; i < this.size; i++) {
475
+ if (A[i] && this.comparator(A[i]!.element, this.min!.element) <= 0) {
476
+ this.min = A[i]!;
477
+ }
478
+ }
479
+ }
480
+
481
+ /**
482
+ * O(log n) time operation.
483
+ * Remove and return the top element (smallest or largest element) from the heap.
484
+ * @returns The top element or undefined if the heap is empty.
485
+ */
486
+ poll(): E | undefined {
487
+ return this.pop();
488
+ }
489
+
490
+ /**
491
+ * O(log n) time operation.
492
+ * Remove and return the top element (smallest or largest element) from the heap.
493
+ * @returns The top element or undefined if the heap is empty.
494
+ */
495
+ pop(): E | undefined {
496
+ if (this.size === 0) return undefined;
497
+
498
+ const z = this.min!;
499
+ if (z.child) {
500
+ const nodes = this.consumeLinkedList(z.child);
501
+ for (const node of nodes) {
502
+ this.mergeWithRoot(node);
503
+ node.parent = undefined;
504
+ }
505
+ }
506
+
507
+ this.removeFromRoot(z);
508
+
509
+ if (z === z.right) {
510
+ this.min = undefined;
511
+ this.root = undefined;
512
+ } else {
513
+ this.min = z.right;
514
+ this.consolidate();
515
+ }
516
+
517
+ this.size--;
518
+
519
+ return z.element;
520
+ }
521
+
522
+ /**
523
+ * O(log n) time operation.
524
+ * merge two heaps. The heap that is merged will be cleared. The heap that is merged into will remain.
525
+ * @param heapToMerge
526
+ */
527
+ merge(heapToMerge: FibonacciHeap<E>): void {
528
+ if (heapToMerge.size === 0) {
529
+ return; // Nothing to merge
530
+ }
531
+
532
+ // Merge the root lists of the two heaps
533
+ if (this.root && heapToMerge.root) {
534
+ const thisRoot = this.root;
535
+ const otherRoot = heapToMerge.root;
536
+
537
+ const thisRootRight = thisRoot.right!;
538
+ const otherRootLeft = otherRoot.left!;
539
+
540
+ thisRoot.right = otherRoot;
541
+ otherRoot.left = thisRoot;
542
+
543
+ thisRootRight.left = otherRootLeft;
544
+ otherRootLeft.right = thisRootRight;
545
+ }
546
+
547
+ // Update the minimum node
548
+ if (!this.min || (heapToMerge.min && this.comparator(heapToMerge.min.element, this.min.element) < 0)) {
549
+ this.min = heapToMerge.min;
550
+ }
551
+
552
+ // Update the size
553
+ this.size += heapToMerge.size;
554
+
555
+ // Clear the heap that was merged
556
+ heapToMerge.clear();
557
+ }
558
+ }
@@ -0,0 +1 @@
1
+ export * from './utils';
@@ -0,0 +1 @@
1
+ export type AnyFunction = (...args: any[]) => any;
@@ -0,0 +1 @@
1
+ export * from './big-o';
@@ -1,4 +1,5 @@
1
- import {MaxHeap, MinHeap} from '../../../../src';
1
+ import {FibonacciHeap, MaxHeap, MinHeap} from '../../../../src';
2
+ import {logBigOMetricsWrap} from "../../../utils";
2
3
 
3
4
  describe('Heap Operation Test', () => {
4
5
  it('should numeric heap work well', function () {
@@ -60,3 +61,190 @@ describe('Heap Operation Test', () => {
60
61
  }
61
62
  });
62
63
  });
64
+
65
+ describe('FibonacciHeap', () => {
66
+ let heap: FibonacciHeap<number>;
67
+
68
+ beforeEach(() => {
69
+ heap = new FibonacciHeap<number>();
70
+ });
71
+
72
+ test('push & peek', () => {
73
+ heap.push(10);
74
+ heap.push(5);
75
+ expect(heap.peek()).toBe(5);
76
+ });
77
+
78
+ test('pop', () => {
79
+ heap.push(10);
80
+ heap.push(5);
81
+ heap.push(15);
82
+ expect(heap.pop()).toBe(5);
83
+ expect(heap.pop()).toBe(10);
84
+ expect(heap.pop()).toBe(15);
85
+ });
86
+
87
+ test('pop on an empty heap', () => {
88
+ expect(heap.pop()).toBeUndefined();
89
+ });
90
+
91
+ test('size', () => {
92
+ expect(heap.size).toBe(0);
93
+ heap.push(10);
94
+ expect(heap.size).toBe(1);
95
+ heap.pop();
96
+ expect(heap.size).toBe(0);
97
+ });
98
+
99
+ test('clear', () => {
100
+ heap.push(10);
101
+ heap.push(5);
102
+ heap.clear();
103
+ expect(heap.size).toBe(0);
104
+ expect(heap.peek()).toBeUndefined();
105
+ });
106
+
107
+ test('custom comparator', () => {
108
+ const maxHeap = new FibonacciHeap<number>((a, b) => b - a);
109
+ maxHeap.push(10);
110
+ maxHeap.push(5);
111
+ expect(maxHeap.peek()).toBe(10);
112
+ });
113
+ });
114
+
115
+ describe('FibonacciHeap', () => {
116
+ let heap: FibonacciHeap<number>;
117
+
118
+ beforeEach(() => {
119
+ heap = new FibonacciHeap<number>();
120
+ });
121
+
122
+ it('should initialize an empty heap', () => {
123
+ expect(heap.size).toBe(0);
124
+ expect(heap.peek()).toBeUndefined();
125
+ });
126
+
127
+ it('should push items into the heap and update size', () => {
128
+ heap.push(10);
129
+ heap.push(5);
130
+
131
+ expect(heap.size).toBe(2);
132
+ });
133
+
134
+ it('should peek the minimum item', () => {
135
+ heap.push(10);
136
+ heap.push(5);
137
+ heap.push(15);
138
+
139
+ expect(heap.peek()).toBe(5);
140
+ });
141
+
142
+ it('should pop the minimum item and update size', () => {
143
+ heap.push(10);
144
+ heap.push(5);
145
+ heap.push(15);
146
+
147
+ const minItem = heap.pop();
148
+
149
+ expect(minItem).toBe(5);
150
+ expect(heap.size).toBe(2);
151
+ });
152
+
153
+ it('should correctly merge two heaps', () => {
154
+ const heap1 = new FibonacciHeap<number>();
155
+ const heap2 = new FibonacciHeap<number>();
156
+
157
+ heap1.push(10);
158
+ heap2.push(5);
159
+
160
+ heap1.merge(heap2);
161
+
162
+ expect(heap1.size).toBe(2);
163
+ expect(heap1.peek()).toBe(5);
164
+ });
165
+
166
+ it('should clear the heap', () => {
167
+ heap.push(10);
168
+ heap.push(5);
169
+
170
+ heap.clear();
171
+
172
+ expect(heap.size).toBe(0);
173
+ expect(heap.peek()).toBeUndefined();
174
+ });
175
+
176
+ it('should handle custom comparators', () => {
177
+ const customComparator = (a: number, b: number) => b - a;
178
+ const customHeap = new FibonacciHeap<number>(customComparator);
179
+
180
+ customHeap.push(10);
181
+ customHeap.push(5);
182
+ customHeap.push(15);
183
+
184
+ expect(customHeap.peek()).toBe(15);
185
+ });
186
+
187
+ describe('FibonacciHeap Merge', () => {
188
+ it('should merge two Fibonacci heaps correctly', () => {
189
+ const heap1 = new FibonacciHeap<number>();
190
+ heap1.push(5).push(10);
191
+
192
+ const heap2 = new FibonacciHeap<number>();
193
+ heap2.push(3).push(7);
194
+
195
+ heap1.merge(heap2);
196
+
197
+ expect(heap1.size).toBe(4); // Combined size of both heaps
198
+ expect(heap2.size).toBe(0); // Merged heap should be empty
199
+ expect(heap1.peek()).toBe(3); // Minimum element should be 3
200
+ });
201
+ });
202
+ });
203
+
204
+
205
+ describe('FibonacciHeap Stress Test', () => {
206
+ it('should handle a large number of elements efficiently', () => {
207
+
208
+ const testByMagnitude = (magnitude: number) => {
209
+ const heap = new FibonacciHeap<number>();
210
+
211
+ // Add 1000 elements to the heap
212
+ for (let i = 1; i <= magnitude; i++) {
213
+ heap.push(i);
214
+ }
215
+
216
+ // Verify that the minimum element is 1 (smallest element)
217
+ expect(heap.peek()).toBe(1);
218
+
219
+ // Remove all 1000 elements from the heap
220
+ const elements = [];
221
+ while (heap.size > 0) {
222
+ elements.push(heap.pop());
223
+ }
224
+
225
+ // Verify that all elements were removed in ascending order
226
+ for (let i = 1; i <= magnitude; i++) {
227
+ expect(elements[i - 1]).toBe(i);
228
+ }
229
+
230
+ // Verify that the heap is now empty
231
+ expect(heap.size).toBe(0);
232
+ }
233
+
234
+ testByMagnitude(1000);
235
+
236
+ // [
237
+ // 10, 100, 1000, 5000, 10000, 20000, 50000, 75000, 100000,
238
+ // 150000, 200000, 250000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000
239
+ // ].forEach(m => logBigOMetricsWrap<typeof testByMagnitude>(testByMagnitude, [m]));
240
+ [
241
+ 10, 100, 1000, 5000, 10000, 20000, 50000, 75000, 100000,
242
+ 150000, 200000, 250000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000
243
+ ].forEach(m => logBigOMetricsWrap((c: number) => {
244
+ const result: number[] = [];
245
+ for (let i = 0; i < c; i++) result.push(i);
246
+ return result;
247
+ } , [m], 'loopPush'));
248
+
249
+ });
250
+ });