Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 10e633f

Browse files
kersovtrekhleb
authored andcommitted
Add MaxHeap (trekhleb#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

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

Lines changed: 175 additions & 0 deletions
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;

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

Lines changed: 104 additions & 0 deletions
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)

AltStyle によって変換されたページ (->オリジナル) /