Skip to content

Commit 9c1b8e4

Browse files
committed
Make it possible to remove from Heap.
1 parent c0fe2a3 commit 9c1b8e4

File tree

2 files changed

+118
-23
lines changed

2 files changed

+118
-23
lines changed

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

+72-20
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,31 @@ export default class MinHeap {
1414
* @param {number} parentIndex
1515
* @return {number}
1616
*/
17-
static getLeftChildIndex(parentIndex) {
17+
getLeftChildIndex(parentIndex) {
1818
return (2 * parentIndex) + 1;
1919
}
2020

2121
/**
2222
* @param {number} parentIndex
2323
* @return {number}
2424
*/
25-
static getRightChildIndex(parentIndex) {
25+
getRightChildIndex(parentIndex) {
2626
return (2 * parentIndex) + 2;
2727
}
2828

2929
/**
3030
* @param {number} childIndex
3131
* @return {number}
3232
*/
33-
static getParentIndex(childIndex) {
33+
getParentIndex(childIndex) {
3434
return Math.floor((childIndex - 1) / 2);
3535
}
3636

3737
/**
3838
* @param {number} childIndex
3939
* @return {boolean}
4040
*/
41-
static hasParent(childIndex) {
41+
hasParent(childIndex) {
4242
return this.getParentIndex(childIndex) >= 0;
4343
}
4444

@@ -47,39 +47,39 @@ export default class MinHeap {
4747
* @return {boolean}
4848
*/
4949
hasLeftChild(parentIndex) {
50-
return MinHeap.getLeftChildIndex(parentIndex) < this.heapContainer.length;
50+
return this.getLeftChildIndex(parentIndex) < this.heapContainer.length;
5151
}
5252

5353
/**
5454
* @param {number} parentIndex
5555
* @return {boolean}
5656
*/
5757
hasRightChild(parentIndex) {
58-
return MinHeap.getRightChildIndex(parentIndex) < this.heapContainer.length;
58+
return this.getRightChildIndex(parentIndex) < this.heapContainer.length;
5959
}
6060

6161
/**
6262
* @param {number} parentIndex
6363
* @return {*}
6464
*/
6565
leftChild(parentIndex) {
66-
return this.heapContainer[MinHeap.getLeftChildIndex(parentIndex)];
66+
return this.heapContainer[this.getLeftChildIndex(parentIndex)];
6767
}
6868

6969
/**
7070
* @param {number} parentIndex
7171
* @return {*}
7272
*/
7373
rightChild(parentIndex) {
74-
return this.heapContainer[MinHeap.getRightChildIndex(parentIndex)];
74+
return this.heapContainer[this.getRightChildIndex(parentIndex)];
7575
}
7676

7777
/**
7878
* @param {number} childIndex
7979
* @return {*}
8080
*/
8181
parent(childIndex) {
82-
return this.heapContainer[MinHeap.getParentIndex(childIndex)];
82+
return this.heapContainer[this.getParentIndex(childIndex)];
8383
}
8484

8585
/**
@@ -126,17 +126,63 @@ export default class MinHeap {
126126

127127
/**
128128
* @param {*} item
129+
* @return {MinHeap}
129130
*/
130131
add(item) {
131132
this.heapContainer.push(item);
132133
this.heapifyUp();
134+
return this;
135+
}
136+
137+
/**
138+
* @param {*} item
139+
* @return {MinHeap}
140+
*/
141+
remove(item) {
142+
// Find number of items to remove.
143+
const numberOfItemsToRemove = this.find(item).length;
144+
145+
for (let iteration = 0; iteration < numberOfItemsToRemove; iteration += 1) {
146+
// We need to find item index to remove each time after removal since
147+
// indices are being change after each heapify process.
148+
const indexToRemove = this.find(item).pop();
149+
150+
// If we need to remove last child in the heap then just remove it.
151+
// There is no need to heapify the heap afterwards.
152+
if (indexToRemove === (this.heapContainer.length - 1)) {
153+
this.heapContainer.pop();
154+
} else {
155+
// Move last element in heap to the vacant (removed) position.
156+
this.heapContainer[indexToRemove] = this.heapContainer.pop();
157+
158+
// Get parent.
159+
const parentItem = this.hasParent(indexToRemove) ? this.parent(indexToRemove) : null;
160+
const leftChild = this.hasLeftChild(indexToRemove) ? this.leftChild(indexToRemove) : null;
161+
162+
// If there is no parent or parent is less then node to delete then heapify down.
163+
// Otherwise heapify up.
164+
if (
165+
leftChild !== null &&
166+
(
167+
parentItem === null ||
168+
this.compare.lessThen(parentItem, this.heapContainer[indexToRemove])
169+
)
170+
) {
171+
this.heapifyDown(indexToRemove);
172+
} else {
173+
this.heapifyUp(indexToRemove);
174+
}
175+
}
176+
}
177+
178+
return this;
133179
}
134180

135181
/**
136182
* @param {*} item
137183
* @return {Number[]}
138184
*/
139-
findItem(item) {
185+
find(item) {
140186
const foundItemIndices = [];
141187

142188
for (let itemIndex = 0; itemIndex < this.heapContainer.length; itemIndex += 1) {
@@ -148,35 +194,41 @@ export default class MinHeap {
148194
return foundItemIndices;
149195
}
150196

151-
heapifyUp() {
197+
/**
198+
* @param {number} [customStartIndex]
199+
*/
200+
heapifyUp(customStartIndex) {
152201
// Take last element (last in array or the bottom left in a tree) in
153202
// a heap container and lift him up until we find the parent element
154203
// that is less then the current new one.
155-
let currentIndex = this.heapContainer.length - 1;
204+
let currentIndex = customStartIndex || this.heapContainer.length - 1;
156205

157206
while (
158-
MinHeap.hasParent(currentIndex) &&
207+
this.hasParent(currentIndex) &&
159208
this.compare.lessThen(this.heapContainer[currentIndex], this.parent(currentIndex))
160209
) {
161-
this.swap(currentIndex, MinHeap.getParentIndex(currentIndex));
162-
currentIndex = MinHeap.getParentIndex(currentIndex);
210+
this.swap(currentIndex, this.getParentIndex(currentIndex));
211+
currentIndex = this.getParentIndex(currentIndex);
163212
}
164213
}
165214

166-
heapifyDown() {
215+
/**
216+
* @param {number} [customStartIndex]
217+
*/
218+
heapifyDown(customStartIndex) {
167219
// Compare the root element to its children and swap root with the smallest
168220
// of children. Do the same for next children after swap.
169-
let currentIndex = 0;
170-
let nextIndex = 0;
221+
let currentIndex = customStartIndex || 0;
222+
let nextIndex = null;
171223

172224
while (this.hasLeftChild(currentIndex)) {
173225
if (
174226
this.hasRightChild(currentIndex) &&
175227
this.compare.lessThen(this.rightChild(currentIndex), this.leftChild(currentIndex))
176228
) {
177-
nextIndex = MinHeap.getRightChildIndex(currentIndex);
229+
nextIndex = this.getRightChildIndex(currentIndex);
178230
} else {
179-
nextIndex = MinHeap.getLeftChildIndex(currentIndex);
231+
nextIndex = this.getLeftChildIndex(currentIndex);
180232
}
181233

182234
if (this.compare.lessThen(this.heapContainer[currentIndex], this.heapContainer[nextIndex])) {

Diff for: src/data-structures/heap/__test__/MinHeap.test.js

+46-3
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,51 @@ describe('MinHeap', () => {
100100

101101
expect(minHeap.toString()).toBe('3,11,10,12,11');
102102

103-
expect(minHeap.findItem(5)).toEqual([]);
104-
expect(minHeap.findItem(3)).toEqual([0]);
105-
expect(minHeap.findItem(11)).toEqual([1, 4]);
103+
expect(minHeap.find(5)).toEqual([]);
104+
expect(minHeap.find(3)).toEqual([0]);
105+
expect(minHeap.find(11)).toEqual([1, 4]);
106+
});
107+
108+
it('should be possible to remove items from heap with heapify down', () => {
109+
const minHeap = new MinHeap();
110+
111+
minHeap.add(3);
112+
minHeap.add(12);
113+
minHeap.add(10);
114+
minHeap.add(11);
115+
minHeap.add(11);
116+
117+
expect(minHeap.toString()).toBe('3,11,10,12,11');
118+
119+
expect(minHeap.remove(3).toString()).toEqual('10,11,11,12');
120+
expect(minHeap.remove(3).peek()).toEqual(10);
121+
expect(minHeap.remove(11).toString()).toEqual('10,12');
122+
expect(minHeap.remove(3).peek()).toEqual(10);
123+
});
124+
125+
it('should be possible to remove items from heap with heapify up', () => {
126+
const minHeap = new MinHeap();
127+
128+
minHeap.add(3);
129+
minHeap.add(10);
130+
minHeap.add(5);
131+
minHeap.add(6);
132+
minHeap.add(7);
133+
minHeap.add(4);
134+
minHeap.add(6);
135+
minHeap.add(8);
136+
minHeap.add(2);
137+
minHeap.add(1);
138+
139+
expect(minHeap.toString()).toBe('1,2,4,6,3,5,6,10,8,7');
140+
expect(minHeap.remove(8).toString()).toEqual('1,2,4,6,3,5,6,10,7');
141+
expect(minHeap.remove(7).toString()).toEqual('1,2,4,6,3,5,6,10');
142+
expect(minHeap.remove(1).toString()).toEqual('2,3,4,6,10,5,6');
143+
expect(minHeap.remove(2).toString()).toEqual('3,6,4,6,10,5');
144+
expect(minHeap.remove(6).toString()).toEqual('3,5,4,10');
145+
expect(minHeap.remove(10).toString()).toEqual('3,5,4');
146+
expect(minHeap.remove(5).toString()).toEqual('3,4');
147+
expect(minHeap.remove(3).toString()).toEqual('4');
148+
expect(minHeap.remove(4).toString()).toEqual('');
106149
});
107150
});

0 commit comments

Comments
 (0)