Skip to content

Commit f499d1b

Browse files
committed
Add heapify method with api docs
1 parent b0e3ca8 commit f499d1b

File tree

2 files changed

+106
-34
lines changed

2 files changed

+106
-34
lines changed

data_structures/non_linear/trees/heaps/max_binary_heap.dart

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,31 @@ import 'package:test/test.dart';
33
import '../../../../utils/list_extension.dart';
44

55
class MaxBinaryHeap {
6-
final List<num> _heap = [];
6+
MaxBinaryHeap() : _heap = [];
7+
8+
MaxBinaryHeap.buildFromArray(this._heap) {
9+
_heapify();
10+
}
11+
12+
final List<num> _heap;
13+
14+
/// Heapify describes the act of taking an existing, unordered array, and transforming it into a Heap structure.
15+
/// What makes this process intriguing, is that if implemented well, it can be done in place, meaning O(1) space,
16+
/// and in linear O(n) time versus the expected O(n log n) time "The naive iterate & insert approach".
17+
///
18+
/// The reason why the _heapify() method starts at i = (_heap.length ~/ 2) - 1
19+
/// is because this is the index of the last non-leaf node in the heap.
20+
/// Starting from the last non-leaf node, sift it down and working backwards ensures
21+
/// that each node in the heap is visited exactly once during the heapify process.
22+
///
23+
/// Iterate over the array and insert VS overriding the heap array and calling siftDown()
24+
/// for each non-leaf node: https://gist.github.com/AhmedLSayed9/968ba2811252f9bb135f4bd8db311ae1
25+
void _heapify() {
26+
final startingParent = (_heap.length ~/ 2) - 1;
27+
for (int i = startingParent; i >= 0; i--) {
28+
_siftDown(i);
29+
}
30+
}
731

832
/// Pseudocode:
933
/// - Push the value into the values property on the heap.
@@ -48,29 +72,28 @@ class MaxBinaryHeap {
4872

4973
_heap.swap(0, _heap.length - 1);
5074
final oldRoot = _heap.removeLast();
51-
_siftDown();
75+
_siftDown(0);
5276
return oldRoot;
5377
}
5478

55-
_siftDown() {
56-
int parentIndex = 0;
79+
/// _siftDown() is responsible for swapping the current item with its largest
80+
/// child (if any), and recursively calling itself on the new child index
81+
/// until the current item is in its correct position.
82+
_siftDown(int parentIndex) {
83+
final leftChildIndex = parentIndex * 2 + 1;
84+
final rightChildIndex = leftChildIndex + 1;
5785

58-
while (true) {
59-
final leftChildIndex = parentIndex * 2 + 1;
60-
final rightChildIndex = leftChildIndex + 1;
86+
if (leftChildIndex >= _heap.length) return;
6187

62-
if (leftChildIndex >= _heap.length) break;
88+
final maxChildIndex = rightChildIndex < _heap.length &&
89+
_heap[rightChildIndex] > _heap[leftChildIndex]
90+
? rightChildIndex
91+
: leftChildIndex;
6392

64-
final maxChildIndex = rightChildIndex < _heap.length &&
65-
_heap[rightChildIndex] > _heap[leftChildIndex]
66-
? rightChildIndex
67-
: leftChildIndex;
93+
if (_heap[maxChildIndex] <= _heap[parentIndex]) return;
6894

69-
if (_heap[maxChildIndex] <= _heap[parentIndex]) break;
70-
71-
_heap.swap(parentIndex, maxChildIndex);
72-
parentIndex = maxChildIndex;
73-
}
95+
_heap.swap(parentIndex, maxChildIndex);
96+
_siftDown(maxChildIndex);
7497
}
7598
}
7699

@@ -118,4 +141,17 @@ void main() {
118141
expect(heap.extract(), isNull);
119142
});
120143
});
144+
145+
group('heapify', () {
146+
test('should convert the list into MaxBinaryHeap', () {
147+
// 2
148+
// 1 6
149+
// 3 5
150+
final heap = MaxBinaryHeap.buildFromArray([2, 1, 6, 3, 5]);
151+
// 6
152+
// 5 2
153+
// 3 1
154+
expect(heap._heap, [6, 5, 2, 3, 1]);
155+
});
156+
});
121157
}

data_structures/non_linear/trees/heaps/min_binary_heap.dart

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,31 @@ import 'package:test/test.dart';
33
import '../../../../utils/list_extension.dart';
44

55
class MinBinaryHeap {
6-
final List<num> _heap = [];
6+
MinBinaryHeap() : _heap = [];
7+
8+
MinBinaryHeap.buildFromArray(this._heap) {
9+
_heapify();
10+
}
11+
12+
final List<num> _heap;
13+
14+
/// Heapify describes the act of taking an existing, unordered array, and transforming it into a Heap structure.
15+
/// What makes this process intriguing, is that if implemented well, it can be done in place, meaning O(1) space,
16+
/// and in linear O(n) time versus the expected O(n log n) time "The naive iterate & insert approach".
17+
///
18+
/// The reason why the _heapify() method starts at i = (_heap.length ~/ 2) - 1
19+
/// is because this is the index of the last non-leaf node in the heap.
20+
/// Starting from the last non-leaf node, sift it down and working backwards ensures
21+
/// that each node in the heap is visited exactly once during the heapify process.
22+
///
23+
/// Iterate over the array and insert VS overriding the heap array and calling siftDown()
24+
/// for each non-leaf node: https://gist.github.com/AhmedLSayed9/968ba2811252f9bb135f4bd8db311ae1
25+
void _heapify() {
26+
final startingParent = (_heap.length ~/ 2) - 1;
27+
for (int i = startingParent; i >= 0; i--) {
28+
_siftDown(i);
29+
}
30+
}
731

832
/// Pseudocode:
933
/// - Push the value into the values property on the heap.
@@ -48,29 +72,28 @@ class MinBinaryHeap {
4872

4973
_heap.swap(0, _heap.length - 1);
5074
final oldRoot = _heap.removeLast();
51-
_siftDown();
75+
_siftDown(0);
5276
return oldRoot;
5377
}
5478

55-
_siftDown() {
56-
int parentIndex = 0;
79+
/// _siftDown() is responsible for swapping the current item with its smallest
80+
/// child (if any), and recursively calling itself on the new child index
81+
/// until the current item is in its correct position.
82+
_siftDown(int parentIndex) {
83+
final leftChildIndex = parentIndex * 2 + 1;
84+
final rightChildIndex = leftChildIndex + 1;
5785

58-
while (true) {
59-
final leftChildIndex = parentIndex * 2 + 1;
60-
final rightChildIndex = leftChildIndex + 1;
86+
if (leftChildIndex >= _heap.length) return;
6187

62-
if (leftChildIndex >= _heap.length) break;
88+
final minChildIndex = rightChildIndex < _heap.length &&
89+
_heap[rightChildIndex] < _heap[leftChildIndex]
90+
? rightChildIndex
91+
: leftChildIndex;
6392

64-
final maxChildIndex = rightChildIndex < _heap.length &&
65-
_heap[rightChildIndex] < _heap[leftChildIndex]
66-
? rightChildIndex
67-
: leftChildIndex;
93+
if (_heap[minChildIndex] >= _heap[parentIndex]) return;
6894

69-
if (_heap[maxChildIndex] >= _heap[parentIndex]) break;
70-
71-
_heap.swap(parentIndex, maxChildIndex);
72-
parentIndex = maxChildIndex;
73-
}
95+
_heap.swap(parentIndex, minChildIndex);
96+
_siftDown(minChildIndex);
7497
}
7598
}
7699

@@ -117,5 +140,18 @@ void main() {
117140

118141
expect(heap.extract(), isNull);
119142
});
143+
144+
group('heapify', () {
145+
test('should convert the list into MinBinaryHeap', () {
146+
// 5
147+
// 4 3
148+
// 1 2
149+
final heap = MinBinaryHeap.buildFromArray([5, 4, 3, 1, 2]);
150+
// 1
151+
// 2 3
152+
// 4 5
153+
expect(heap._heap, [1, 2, 3, 4, 5]);
154+
});
155+
});
120156
});
121157
}

0 commit comments

Comments
 (0)