Skip to content

Commit 827177f

Browse files
committed
feat(heap): add error handling for heaps
BREAKING CHANGE: size is now an attribute rather than a method. Similar to the built-in Map.size and Set.size
1 parent 3a2a24f commit 827177f

File tree

4 files changed

+41
-29
lines changed

4 files changed

+41
-29
lines changed

src/data-structures/heaps/heap.js

+12-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
class Heap {
99
constructor(comparator = (a, b) => a - b) {
1010
this.array = [];
11-
this.comparator = (i1, i2) => comparator(this.array[i1], this.array[i2]);
11+
this.comparator = (i1, i2) => {
12+
const value = comparator(this.array[i1], this.array[i2]);
13+
if (Number.isNaN(value)) { throw new Error(`Comparator should evaluate to a number. Got ${value} when comparing ${this.array[i1]} with ${this.array[i2]}`); }
14+
return value;
15+
};
1216
}
1317

1418
/**
@@ -34,8 +38,8 @@ class Heap {
3438
* @runtime O(log n)
3539
*/
3640
remove(index = 0) {
37-
if (!this.size()) return null;
38-
this.swap(index, this.size() - 1); // swap with last
41+
if (!this.size) return null;
42+
this.swap(index, this.size - 1); // swap with last
3943
const value = this.array.pop(); // remove element
4044
this.bubbleDown(index);
4145
return value;
@@ -45,7 +49,7 @@ class Heap {
4549
* Returns the number of elements in this collection.
4650
* @runtime O(1)
4751
*/
48-
size() {
52+
get size() {
4953
return this.array.length;
5054
}
5155

@@ -54,7 +58,7 @@ class Heap {
5458
* @runtime O(log n)
5559
*/
5660
bubbleUp() {
57-
let index = this.size() - 1;
61+
let index = this.size - 1;
5862
const parent = (i) => Math.ceil(i / 2 - 1);
5963
while (parent(index) >= 0 && this.comparator(parent(index), index) > 0) {
6064
this.swap(parent(index), index);
@@ -70,18 +74,18 @@ class Heap {
7074
let curr = index;
7175
const left = (i) => 2 * i + 1;
7276
const right = (i) => 2 * i + 2;
73-
const getTopChild = (i) => (right(i) < this.size()
77+
const getTopChild = (i) => (right(i) < this.size
7478
&& this.comparator(left(i), right(i)) > 0 ? right(i) : left(i));
7579

76-
while (left(curr) < this.size() && this.comparator(curr, getTopChild(curr)) > 0) {
80+
while (left(curr) < this.size && this.comparator(curr, getTopChild(curr)) > 0) {
7781
const next = getTopChild(curr);
7882
this.swap(curr, next);
7983
curr = next;
8084
}
8185
}
8286

8387
/**
84-
* "Private": Swap elements on the heap
88+
* Swap elements on the heap
8589
* @runtime O(1)
8690
* @param {number} i1 index 1
8791
* @param {number} i2 index 2

src/data-structures/heaps/heap.spec.js

+21-13
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ const PriorityQueue = require('./priority-queue');
33
const MaxHeap = require('./max-heap');
44
const MinHeap = require('./min-heap');
55

6-
[[Heap], [PriorityQueue], [MinHeap]].forEach(([DS, arg]) => {
7-
describe('Min-Heap (Priority Queue)', () => {
6+
[
7+
[Heap],
8+
[PriorityQueue, [], (a, b) => a - b],
9+
[MinHeap],
10+
].forEach(([DS, ...arg]) => {
11+
describe('Min-Heap and Priority Queue', () => {
812
let heap;
913

1014
beforeEach(() => {
11-
heap = new DS(arg);
15+
heap = new DS(...arg);
1216
});
1317

1418
describe('#contructor', () => {
@@ -21,7 +25,7 @@ const MinHeap = require('./min-heap');
2125
it('should add an element', () => {
2226
expect(heap.add(1)).toBe(undefined);
2327
expect(heap.array).toEqual([1]);
24-
expect(heap.size()).toBe(1);
28+
expect(heap.size).toBe(1);
2529
});
2630

2731
it('should keep things in order', () => {
@@ -31,7 +35,7 @@ const MinHeap = require('./min-heap');
3135
expect(heap.array[0]).toEqual(2);
3236
heap.add(1);
3337
expect(heap.array[0]).toEqual(1);
34-
expect(heap.size()).toEqual(3);
38+
expect(heap.size).toEqual(3);
3539
});
3640
});
3741

@@ -40,7 +44,7 @@ const MinHeap = require('./min-heap');
4044
heap.add(1);
4145
heap.add(0);
4246
expect(heap.remove()).toBe(0);
43-
expect(heap.size()).toBe(1);
47+
expect(heap.size).toBe(1);
4448
expect(heap.array).toEqual([1]);
4549
});
4650

@@ -70,19 +74,23 @@ const MinHeap = require('./min-heap');
7074
expect(heap.remove()).toEqual(1);
7175
expect(heap.remove()).toEqual(2);
7276
expect(heap.remove()).toEqual(3);
73-
expect(heap.size()).toBe(0);
77+
expect(heap.size).toBe(0);
7478
});
7579
});
7680
});
7781
});
7882
});
7983

80-
[[Heap, (a, b) => b - a], [PriorityQueue, (a, b) => b - a], [MaxHeap]].forEach(([DS, arg]) => {
84+
[
85+
[Heap, (a, b) => b - a],
86+
[PriorityQueue, [], (a, b) => b - a],
87+
[MaxHeap],
88+
].forEach(([DS, ...arg]) => {
8189
describe('Max-Heap (Priority Queue)', () => {
8290
let heap;
8391

8492
beforeEach(() => {
85-
heap = new DS(arg);
93+
heap = new DS(...arg);
8694
});
8795

8896
describe('#contructor', () => {
@@ -95,7 +103,7 @@ const MinHeap = require('./min-heap');
95103
it('should add an element', () => {
96104
expect(heap.add(1)).toBe(undefined);
97105
expect(heap.array).toEqual([1]);
98-
expect(heap.size()).toBe(1);
106+
expect(heap.size).toBe(1);
99107
});
100108

101109
it('should keep things in order', () => {
@@ -105,7 +113,7 @@ const MinHeap = require('./min-heap');
105113
expect(heap.array[0]).toEqual(2);
106114
heap.add(3);
107115
expect(heap.array[0]).toEqual(3);
108-
expect(heap.size()).toEqual(3);
116+
expect(heap.size).toEqual(3);
109117
});
110118
});
111119

@@ -114,7 +122,7 @@ const MinHeap = require('./min-heap');
114122
heap.add(1);
115123
heap.add(0);
116124
expect(heap.remove()).toBe(1);
117-
expect(heap.size()).toBe(1);
125+
expect(heap.size).toBe(1);
118126
expect(heap.array).toEqual([0]);
119127
});
120128

@@ -156,7 +164,7 @@ const MinHeap = require('./min-heap');
156164
expect(heap.remove()).toEqual(2);
157165
expect(heap.remove()).toEqual(1);
158166
expect(heap.remove()).toEqual(0);
159-
expect(heap.size()).toBe(0);
167+
expect(heap.size).toBe(0);
160168
});
161169
});
162170
});

src/data-structures/heaps/median-heap.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ class MedianHeap {
3030
}
3131

3232
// rebalance if the sizes of the heaps differ by more than one element
33-
if (Math.abs(this.min.size() - this.max.size()) > 1) {
33+
if (Math.abs(this.min.size - this.max.size) > 1) {
3434
// extract the min/max from the heap with more elements and insert it into the other heap.
35-
if (this.min.size() > this.max.size()) {
35+
if (this.min.size > this.max.size) {
3636
this.max.add(this.min.remove());
3737
} else {
3838
this.min.add(this.max.remove());
@@ -47,12 +47,12 @@ class MedianHeap {
4747
findMedian() {
4848
let median;
4949

50-
if (this.max.size() === this.min.size()) {
50+
if (this.max.size === this.min.size) {
5151
// When both heaps contain the same number of elements,
5252
// the total number of elements is even.
5353
// The median is the mean of the two middle elements.
5454
median = (this.max.peek() + this.min.peek()) / 2;
55-
} else if (this.max.size() > this.min.size()) {
55+
} else if (this.max.size > this.min.size) {
5656
// when the max-heap contains one more element than the min-heap,
5757
// the median is in the top of the max-heap.
5858
median = this.max.peek();
@@ -67,8 +67,8 @@ class MedianHeap {
6767
/**
6868
* Return size of the heap.
6969
*/
70-
size() {
71-
return this.min.size() + this.max.size();
70+
get size() {
71+
return this.min.size + this.max.size;
7272
}
7373
}
7474

src/data-structures/heaps/median-heap.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ describe('Median Heap', () => {
1010
describe('#add', () => {
1111
it('should work', () => {
1212
expect(medianHeap.add(1)).toEqual(undefined);
13-
expect(medianHeap.size()).toEqual(1);
13+
expect(medianHeap.size).toEqual(1);
1414
});
1515

1616
it('should work', () => {
1717
expect(medianHeap.add(1)).toEqual(undefined);
1818
expect(medianHeap.add(1)).toEqual(undefined);
19-
expect(medianHeap.size()).toEqual(2);
19+
expect(medianHeap.size).toEqual(2);
2020
});
2121
});
2222

0 commit comments

Comments
 (0)