Skip to content

Commit 4b953f3

Browse files
codeazqappgurueu
andauthored
feat: add min heap and max heap data structure (TheAlgorithms#117)
* feat: add min heap and max heap data structure * fix: extract common heap operations to base class * add check for heap invariant * move check operations to _check, throw error when check fails * `return` and `&&` are superfluous now * Fix return types * Missed a `return true` --------- Co-authored-by: Lars Müller <[email protected]>
1 parent e85ea1b commit 4b953f3

File tree

5 files changed

+259
-0
lines changed

5 files changed

+259
-0
lines changed

data_structures/heap/heap.ts

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
export abstract class Heap<T> {
2+
protected heap: T[];
3+
4+
constructor(elements: T[] = []) {
5+
this.heap = [];
6+
for (let element of elements) {
7+
this.insert(element);
8+
}
9+
}
10+
11+
/**
12+
* Compares the value at parentIndex with the value at childIndex
13+
* In a maxHeap the value at parentIndex should be larger than the value at childIndex
14+
* In a minHeap the value at parentIndex should be smaller than the value at childIndex
15+
*
16+
*/
17+
protected abstract isRightlyPlaced(
18+
childIndex: number,
19+
parentIndex: number
20+
): boolean;
21+
22+
/**
23+
* In a maxHeap the the index with the larger value in returned
24+
* In a maxHeap the the index with the larger value in returned
25+
*/
26+
protected abstract getChildIndexToSwap(
27+
leftChildIndex: number,
28+
rightChildIndex: number
29+
): number;
30+
31+
public insert(value: T): void {
32+
this.heap.push(value);
33+
this.bubbleUp();
34+
}
35+
36+
public extract(): T {
37+
let maxElement = this.heap[0];
38+
this.heap[0] = this.heap[this.size() - 1];
39+
this.heap.pop();
40+
this.sinkDown();
41+
return maxElement;
42+
}
43+
44+
public size(): number {
45+
return this.heap.length;
46+
}
47+
48+
public isEmpty(): boolean {
49+
return this.size() === 0;
50+
}
51+
52+
protected bubbleUp(): void {
53+
let index = this.size() - 1;
54+
let parentIndex;
55+
56+
while (index > 0) {
57+
parentIndex = Math.floor((index - 1) / 2);
58+
if (this.isRightlyPlaced(index, parentIndex)) break;
59+
[this.heap[parentIndex], this.heap[index]] = [
60+
this.heap[index],
61+
this.heap[parentIndex],
62+
];
63+
index = parentIndex;
64+
}
65+
}
66+
67+
protected sinkDown(): void {
68+
let index = 0;
69+
let leftChildIndex = this.getLeftChildIndex(index);
70+
let rightChildIndex = this.getRightChildIndex(index);
71+
let childIndexToSwap;
72+
73+
while (this.heap[leftChildIndex] || this.heap[rightChildIndex]) {
74+
childIndexToSwap = this.getChildIndexToSwap(
75+
leftChildIndex,
76+
rightChildIndex
77+
);
78+
if (this.isRightlyPlaced(childIndexToSwap, index)) break;
79+
[this.heap[childIndexToSwap], this.heap[index]] = [
80+
this.heap[index],
81+
this.heap[childIndexToSwap],
82+
];
83+
index = childIndexToSwap;
84+
leftChildIndex = this.getLeftChildIndex(index);
85+
rightChildIndex = this.getRightChildIndex(index);
86+
}
87+
}
88+
89+
protected getLeftChildIndex(index: number): number {
90+
return index * 2 + 1;
91+
}
92+
93+
protected getRightChildIndex(index: number): number {
94+
return index * 2 + 2;
95+
}
96+
97+
public check(): void {
98+
return this._check();
99+
}
100+
101+
protected _check(index: number = 0): void {
102+
if (!this.heap[index]) return;
103+
const leftChildIndex = this.getLeftChildIndex(index);
104+
const rightChildIndex = this.getRightChildIndex(index);
105+
106+
if (
107+
this.heap[leftChildIndex] &&
108+
!this.isRightlyPlaced(leftChildIndex, index)
109+
)
110+
throw new Error("Heap does not adhere to heap invariant");
111+
112+
if (
113+
this.heap[rightChildIndex] &&
114+
!this.isRightlyPlaced(rightChildIndex, index)
115+
)
116+
throw new Error("Heap does not adhere to heap invariant");
117+
118+
this._check(leftChildIndex);
119+
this._check(rightChildIndex);
120+
}
121+
}

data_structures/heap/max_heap.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Heap } from "./heap";
2+
3+
/**
4+
* A heap is a complete binary tree
5+
* In a complete binary tree each level is filled before lower levels are added
6+
* Each level is filled from left to right
7+
*
8+
* In a max heap the value of every node is greater than that if its children
9+
*
10+
* The heap if often implemented using an array structure.
11+
* In the array implementation, the relationship between a parent index and its two children
12+
* are ((parentindex * 2) + 1) and ((parentindex * 2) + 2)
13+
*
14+
*/
15+
export class MaxHeap<T> extends Heap<T> {
16+
constructor(elements: T[] = []) {
17+
super(elements);
18+
}
19+
20+
/**
21+
* Checks if the value at the parent index is larger than or equal to
22+
* the value at the child index
23+
*/
24+
protected isRightlyPlaced(childIndex: number, parentIndex: number): boolean {
25+
return this.heap[parentIndex] >= this.heap[childIndex];
26+
}
27+
28+
/**
29+
* Returns the child index that stores a larger value
30+
*/
31+
protected getChildIndexToSwap(
32+
leftChildIndex: number,
33+
rightChildIndex: number
34+
): number {
35+
return (this.heap[leftChildIndex] || -Infinity) >
36+
(this.heap[rightChildIndex] || -Infinity)
37+
? leftChildIndex
38+
: rightChildIndex;
39+
}
40+
}

data_structures/heap/min_heap.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Heap } from "./heap";
2+
3+
/**
4+
* A heap is a complete binary tree
5+
* In a complete binary tree each level is filled before lower levels are added
6+
* Each level is filled from left to right
7+
*
8+
* In a min heap the value of every node is smaller than that if its children
9+
*
10+
* The heap if often implemented using an array structure.
11+
* In the array implementation, the relationship between a parent index and its two children
12+
* are ((parentindex * 2) + 1) and ((parentindex * 2) + 2)
13+
*
14+
*/
15+
export class MinHeap<T> extends Heap<T> {
16+
constructor(elements: T[] = []) {
17+
super(elements);
18+
}
19+
20+
/**
21+
* Checks if the value at the parent index is lesser than or equal to
22+
* the value at the child index
23+
*/
24+
protected isRightlyPlaced(childIndex: number, parentIndex: number): boolean {
25+
return this.heap[parentIndex] <= this.heap[childIndex];
26+
}
27+
28+
/**
29+
* Returns the child index that stores a smaller value
30+
*/
31+
protected getChildIndexToSwap(
32+
leftChildIndex: number,
33+
rightChildIndex: number
34+
): number {
35+
return (this.heap[leftChildIndex] || -Infinity) <
36+
(this.heap[rightChildIndex] || -Infinity)
37+
? leftChildIndex
38+
: rightChildIndex;
39+
}
40+
}
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { MaxHeap } from "../max_heap";
2+
3+
describe("MaxHeap", () => {
4+
let heap: MaxHeap<number>;
5+
6+
beforeAll(() => {
7+
const elements: number[] = [
8+
12, 4, 43, 42, 9, 7, 39, 16, 55, 1, 51, 34, 81, 18,
9+
];
10+
heap = new MaxHeap(elements);
11+
});
12+
13+
it("should initialize a heap from input array", () => {
14+
expect(heap.isEmpty()).toEqual(false);
15+
heap.check();
16+
});
17+
18+
it("should remove and return the max element in the heap", () => {
19+
const maxValue = heap.extract();
20+
21+
expect(maxValue).toEqual(81);
22+
heap.check();
23+
});
24+
25+
it("should insert a new element and bubble Up the element to it correct index in the heap", () => {
26+
heap.insert(61);
27+
heap.check();
28+
});
29+
});
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { MinHeap } from "../min_heap";
2+
3+
describe("MinHeap", () => {
4+
let heap: MinHeap<number>;
5+
6+
beforeAll(() => {
7+
const elements: number[] = [
8+
12, 4, 43, 42, 9, 7, 39, 16, 55, 1, 51, 34, 81, 18,
9+
];
10+
heap = new MinHeap(elements);
11+
});
12+
13+
it("should initialize a heap from input array", () => {
14+
expect(heap.isEmpty()).toEqual(false);
15+
heap.check();
16+
});
17+
18+
it("should remove and return the min element in the heap", () => {
19+
const minValue = heap.extract();
20+
21+
expect(minValue).toEqual(1);
22+
heap.check();
23+
});
24+
25+
it("should insert a new element and bubble Up the element to it correct index in the heap", () => {
26+
heap.insert(24);
27+
heap.check();
28+
});
29+
});

0 commit comments

Comments
 (0)