Skip to content

Commit a02d5f6

Browse files
committedAug 17, 2018
Move common Max/Min Heap code to Heap.js.
1 parent 031c5da commit a02d5f6

File tree

3 files changed

+126
-182
lines changed

3 files changed

+126
-182
lines changed
 

‎src/data-structures/heap/Heap.js

+108-4
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,52 @@ export default class Heap {
142142
return this;
143143
}
144144

145+
/**
146+
* @param {*} item
147+
* @param {Comparator} [customFindingComparator]
148+
* @return {Heap}
149+
*/
150+
remove(item, customFindingComparator) {
151+
// Find number of items to remove.
152+
const customComparator = customFindingComparator || this.compare;
153+
const numberOfItemsToRemove = this.find(item, customComparator).length;
154+
155+
for (let iteration = 0; iteration < numberOfItemsToRemove; iteration += 1) {
156+
// We need to find item index to remove each time after removal since
157+
// indices are being change after each heapify process.
158+
const indexToRemove = this.find(item, customComparator).pop();
159+
160+
// If we need to remove last child in the heap then just remove it.
161+
// There is no need to heapify the heap afterwards.
162+
if (indexToRemove === (this.heapContainer.length - 1)) {
163+
this.heapContainer.pop();
164+
} else {
165+
// Move last element in heap to the vacant (removed) position.
166+
this.heapContainer[indexToRemove] = this.heapContainer.pop();
167+
168+
// Get parent.
169+
const parentItem = this.hasParent(indexToRemove) ? this.parent(indexToRemove) : null;
170+
const leftChild = this.hasLeftChild(indexToRemove) ? this.leftChild(indexToRemove) : null;
171+
172+
// If there is no parent or parent is in incorrect order with the node
173+
// we're going to delete then heapify down. Otherwise heapify up.
174+
if (
175+
leftChild !== null
176+
&& (
177+
parentItem === null
178+
|| !this.pairIsInCorrectOrder(parentItem, this.heapContainer[indexToRemove])
179+
)
180+
) {
181+
this.heapifyDown(indexToRemove);
182+
} else {
183+
this.heapifyUp(indexToRemove);
184+
}
185+
}
186+
}
187+
188+
return this;
189+
}
190+
145191
/**
146192
* @param {*} item
147193
* @param {Comparator} [customComparator]
@@ -174,11 +220,69 @@ export default class Heap {
174220
return this.heapContainer.toString();
175221
}
176222

177-
heapifyUp() {
178-
throw new Error('You have to implement this method!');
223+
/**
224+
* @param {number} [customStartIndex]
225+
*/
226+
heapifyUp(customStartIndex) {
227+
// Take last element (last in array or the bottom left in a tree) in
228+
// a heap container and lift him up until we find the parent element
229+
// that is less then the current new one.
230+
let currentIndex = customStartIndex || this.heapContainer.length - 1;
231+
232+
while (
233+
this.hasParent(currentIndex)
234+
&& !this.pairIsInCorrectOrder(this.parent(currentIndex), this.heapContainer[currentIndex])
235+
) {
236+
this.swap(currentIndex, this.getParentIndex(currentIndex));
237+
currentIndex = this.getParentIndex(currentIndex);
238+
}
179239
}
180240

181-
heapifyDown() {
182-
throw new Error('You have to implement this method!');
241+
/**
242+
* @param {number} [customStartIndex]
243+
*/
244+
heapifyDown(customStartIndex) {
245+
// Compare the root element to its children and swap root with the smallest
246+
// of children. Do the same for next children after swap.
247+
let currentIndex = customStartIndex || 0;
248+
let nextIndex = null;
249+
250+
while (this.hasLeftChild(currentIndex)) {
251+
if (
252+
this.hasRightChild(currentIndex)
253+
&& this.pairIsInCorrectOrder(this.rightChild(currentIndex), this.leftChild(currentIndex))
254+
) {
255+
nextIndex = this.getRightChildIndex(currentIndex);
256+
} else {
257+
nextIndex = this.getLeftChildIndex(currentIndex);
258+
}
259+
260+
if (!this.pairIsInCorrectOrder(
261+
this.heapContainer[nextIndex],
262+
this.heapContainer[currentIndex],
263+
)) {
264+
break;
265+
}
266+
267+
this.swap(currentIndex, nextIndex);
268+
currentIndex = nextIndex;
269+
}
270+
}
271+
272+
/**
273+
* Checks if pair of heap elements is in correct order.
274+
* For MinHeap the first element must be always smaller or equal.
275+
* For MaxHeap the first element must be always bigger or equal.
276+
*
277+
* @param {*} firstElement
278+
* @param {*} secondElement
279+
* @return {boolean}
280+
*/
281+
/* istanbul ignore next */
282+
pairIsInCorrectOrder(firstElement, secondElement) {
283+
throw new Error(`
284+
You have to implement heap pair comparision method
285+
for ${firstElement} and ${secondElement} values.
286+
`);
183287
}
184288
}

‎src/data-structures/heap/MaxHeap.js

+9-90
Original file line numberDiff line numberDiff line change
@@ -2,96 +2,15 @@ import Heap from './Heap';
22

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

‎src/data-structures/heap/MinHeap.js

+9-88
Original file line numberDiff line numberDiff line change
@@ -2,94 +2,15 @@ import Heap from './Heap';
22

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

0 commit comments

Comments
 (0)
Please sign in to comment.