Skip to content

Commit 10e633f

Browse files
kersovtrekhleb
authored andcommitted
Add MaxHeap (#167)
* Add MaxHeap * Add parent class for MinHeap and MaxHeap
1 parent a191ade commit 10e633f

File tree

4 files changed

+460
-167
lines changed

4 files changed

+460
-167
lines changed

Diff for: src/data-structures/heap/Heap.js

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import Comparator from '../../utils/comparator/Comparator';
2+
3+
/**
4+
* Parent class for heaps
5+
* @class
6+
*/
7+
class Heap {
8+
/**
9+
* @constructs Heap
10+
* @param {Function} [comparatorFunction]
11+
*/
12+
constructor(comparatorFunction) {
13+
// Array representation of the heap.
14+
this.heapContainer = [];
15+
this.compare = new Comparator(comparatorFunction);
16+
}
17+
18+
/**
19+
* @param {number} parentIndex
20+
* @return {number}
21+
*/
22+
getLeftChildIndex(parentIndex) {
23+
return (2 * parentIndex) + 1;
24+
}
25+
26+
/**
27+
* @param {number} parentIndex
28+
* @return {number}
29+
*/
30+
getRightChildIndex(parentIndex) {
31+
return (2 * parentIndex) + 2;
32+
}
33+
34+
/**
35+
* @param {number} childIndex
36+
* @return {number}
37+
*/
38+
getParentIndex(childIndex) {
39+
return Math.floor((childIndex - 1) / 2);
40+
}
41+
42+
/**
43+
* @param {number} childIndex
44+
* @return {boolean}
45+
*/
46+
hasParent(childIndex) {
47+
return this.getParentIndex(childIndex) >= 0;
48+
}
49+
50+
/**
51+
* @param {number} parentIndex
52+
* @return {boolean}
53+
*/
54+
hasLeftChild(parentIndex) {
55+
return this.getLeftChildIndex(parentIndex) < this.heapContainer.length;
56+
}
57+
58+
/**
59+
* @param {number} parentIndex
60+
* @return {boolean}
61+
*/
62+
hasRightChild(parentIndex) {
63+
return this.getRightChildIndex(parentIndex) < this.heapContainer.length;
64+
}
65+
66+
/**
67+
* @param {number} parentIndex
68+
* @return {*}
69+
*/
70+
leftChild(parentIndex) {
71+
return this.heapContainer[this.getLeftChildIndex(parentIndex)];
72+
}
73+
74+
/**
75+
* @param {number} parentIndex
76+
* @return {*}
77+
*/
78+
rightChild(parentIndex) {
79+
return this.heapContainer[this.getRightChildIndex(parentIndex)];
80+
}
81+
82+
/**
83+
* @param {number} childIndex
84+
* @return {*}
85+
*/
86+
parent(childIndex) {
87+
return this.heapContainer[this.getParentIndex(childIndex)];
88+
}
89+
90+
/**
91+
* @param {number} indexOne
92+
* @param {number} indexTwo
93+
*/
94+
swap(indexOne, indexTwo) {
95+
const tmp = this.heapContainer[indexTwo];
96+
this.heapContainer[indexTwo] = this.heapContainer[indexOne];
97+
this.heapContainer[indexOne] = tmp;
98+
}
99+
100+
/**
101+
* @return {*}
102+
*/
103+
peek() {
104+
if (this.heapContainer.length === 0) {
105+
return null;
106+
}
107+
108+
return this.heapContainer[0];
109+
}
110+
111+
/**
112+
* @return {*}
113+
*/
114+
poll() {
115+
if (this.heapContainer.length === 0) {
116+
return null;
117+
}
118+
119+
if (this.heapContainer.length === 1) {
120+
return this.heapContainer.pop();
121+
}
122+
123+
const item = this.heapContainer[0];
124+
125+
// Move the last element from the end to the head.
126+
this.heapContainer[0] = this.heapContainer.pop();
127+
this.heapifyDown();
128+
129+
return item;
130+
}
131+
132+
/**
133+
* @param {*} item
134+
* @return {MinHeap}
135+
*/
136+
add(item) {
137+
this.heapContainer.push(item);
138+
this.heapifyUp();
139+
return this;
140+
}
141+
142+
/**
143+
* @param {*} item
144+
* @param {Comparator} [customComparator]
145+
* @return {Number[]}
146+
*/
147+
find(item, customComparator) {
148+
const foundItemIndices = [];
149+
const comparator = customComparator || this.compare;
150+
151+
for (let itemIndex = 0; itemIndex < this.heapContainer.length; itemIndex += 1) {
152+
if (comparator.equal(item, this.heapContainer[itemIndex])) {
153+
foundItemIndices.push(itemIndex);
154+
}
155+
}
156+
157+
return foundItemIndices;
158+
}
159+
160+
/**
161+
* @return {boolean}
162+
*/
163+
isEmpty() {
164+
return !this.heapContainer.length;
165+
}
166+
167+
/**
168+
* @return {string}
169+
*/
170+
toString() {
171+
return this.heapContainer.toString();
172+
}
173+
}
174+
175+
export default Heap;

Diff for: src/data-structures/heap/MaxHeap.js

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import Heap from './Heap';
2+
3+
/**
4+
* Creates a new MaxHeap
5+
* @class
6+
* @augments Heap
7+
*/
8+
class MaxHeap extends Heap {
9+
/**
10+
* @param {*} item
11+
* @param {Comparator} [customFindingComparator]
12+
* @return {MaxHeap}
13+
*/
14+
remove(item, customFindingComparator) {
15+
// Find number of items to remove.
16+
const customComparator = customFindingComparator || this.compare;
17+
const numberOfItemsToRemove = this.find(item, customComparator).length;
18+
19+
for (let iteration = 0; iteration < numberOfItemsToRemove; iteration += 1) {
20+
// We need to find item index to remove each time after removal since
21+
// indices are being change after each heapify process.
22+
const indexToRemove = this.find(item, customComparator).pop();
23+
24+
// If we need to remove last child in the heap then just remove it.
25+
// There is no need to heapify the heap afterwards.
26+
if (indexToRemove === (this.heapContainer.length - 1)) {
27+
this.heapContainer.pop();
28+
} else {
29+
// Move last element in heap to the vacant (removed) position.
30+
this.heapContainer[indexToRemove] = this.heapContainer.pop();
31+
32+
// Get parent.
33+
const parentItem = this.hasParent(indexToRemove) ? this.parent(indexToRemove) : null;
34+
const leftChild = this.hasLeftChild(indexToRemove) ? this.leftChild(indexToRemove) : null;
35+
36+
// If there is no parent or parent is greater then node to delete then heapify down.
37+
// Otherwise heapify up.
38+
if (
39+
leftChild !== null
40+
&& (
41+
parentItem === null
42+
|| this.compare.greaterThan(parentItem, this.heapContainer[indexToRemove])
43+
)
44+
) {
45+
this.heapifyDown(indexToRemove);
46+
} else {
47+
this.heapifyUp(indexToRemove);
48+
}
49+
}
50+
}
51+
52+
return this;
53+
}
54+
55+
/**
56+
* @param {number} [customStartIndex]
57+
*/
58+
heapifyUp(customStartIndex) {
59+
// Take last element (last in array or the bottom left in a tree) in
60+
// a heap container and lift him up until we find the parent element
61+
// that is greater then the current new one.
62+
let currentIndex = customStartIndex || this.heapContainer.length - 1;
63+
64+
while (
65+
this.hasParent(currentIndex)
66+
&& this.compare.greaterThan(this.heapContainer[currentIndex], this.parent(currentIndex))
67+
) {
68+
this.swap(currentIndex, this.getParentIndex(currentIndex));
69+
currentIndex = this.getParentIndex(currentIndex);
70+
}
71+
}
72+
73+
/**
74+
* @param {number} [customStartIndex]
75+
*/
76+
heapifyDown(customStartIndex) {
77+
// Compare the root element to its children and swap root with the smallest
78+
// of children. Do the same for next children after swap.
79+
let currentIndex = customStartIndex || 0;
80+
let nextIndex = null;
81+
82+
while (this.hasLeftChild(currentIndex)) {
83+
if (
84+
this.hasRightChild(currentIndex)
85+
&& this.compare.greaterThan(this.rightChild(currentIndex), this.leftChild(currentIndex))
86+
) {
87+
nextIndex = this.getRightChildIndex(currentIndex);
88+
} else {
89+
nextIndex = this.getLeftChildIndex(currentIndex);
90+
}
91+
92+
if (
93+
this.compare.greaterThan(this.heapContainer[currentIndex], this.heapContainer[nextIndex])
94+
) {
95+
break;
96+
}
97+
98+
this.swap(currentIndex, nextIndex);
99+
currentIndex = nextIndex;
100+
}
101+
}
102+
}
103+
104+
export default MaxHeap;

0 commit comments

Comments
 (0)