Repository navigation
Expand file tree
/
Copy pathMax_binary_heap.js
More file actions
324 lines (293 loc) · 9.75 KB
/
Copy pathMax_binary_heap.js
File metadata and controls
324 lines (293 loc) · 9.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/**
* ============================================================================
* MAX BINARY HEAP (Array-Based)
* ============================================================================
*
* PROBLEM STATEMENT:
* Maintain a collection that supports:
* - Insert a value in O(log n)
* - Extract the maximum value in O(log n)
* - Maximum is always at the root (index 0)
*
* PATTERN (ARRAY ↔ TREE):
* - Complete binary tree stored in an array.
* - For index i: parent at floor((i-1)/2), left child at 2*i+1, right at 2*i+2.
* - Insert: append then bubble up (swap with parent until parent >= element).
* - Extract max: replace root with last element, then sink down (swap with
* larger child until both children are <= element).
*
* Simple visual (index in brackets):
*
* 55[0]
* / \
* 39[1] 33[2]
* / \ /
* 18[3] 27[4] 12[5]
*
* Array view: [55, 39, 33, 18, 27, 12]
*
* REAL-WORLD ANALOGIES:
* - Task scheduler: "Always run the highest-priority task next" (extractMax).
* - Leaderboard: "Insert new score, then get current top score" (insert + peek).
* - Heap sort: Build max-heap, repeatedly extractMax to get sorted order.
*
* WHEN TO USE:
* - Priority queue where highest priority should be served first.
* - Top-K largest (use min-heap of size k for that; max-heap for "global" max).
*
* Time: insert O(log n), extractMax O(log n). Space: O(n).
*/
// ============================================================================
// MaxBinaryHeap implementation
// ============================================================================
/**
* Swap two elements in an array.
* @param {number[]} arr
* @param {number} i
* @param {number} j
*/
function swap(arr, i, j) {
if (i === j) return;
[arr[i], arr[j]] = [arr[j], arr[i]];
}
/**
* Max Binary Heap: parent >= children; root is the maximum.
*/
class MaxBinaryHeap {
constructor() {
/** @type {number[]} */
this.values = [];
}
/**
* Insert a value and restore max-heap property by bubbling up.
*
* @example
* // Real-world: Add a new task with priority; heap keeps highest at root.
* const scheduler = new MaxBinaryHeap();
* scheduler.insert(41);
* scheduler.insert(55);
* scheduler.extractMax(); // 55 (highest priority task runs first)
*
* @param {number} element - Value to insert.
*/
insert(element) {
this.values.push(element);
// Default: iterative bubble-up (interview-friendly).
this.bubbleUp(this.values.length - 1);
}
/**
* Iterative bubble up: while current node is larger than its parent,
* keep swapping upwards.
*
* VISUAL TRACE (array → tree, after inserting 55 into [41, 39, 33, 18, 27, 12]):
*
* Step 0: [41, 39, 33, 18, 27, 12, 55]
* 41
* / \
* 39 33
* / \ / \
* 18 27 12 55
*
* Compare 55 with parent 33 → swap:
*
* Step 1: [41, 39, 55, 18, 27, 12, 33]
* 41
* / \
* 39 55
* / \ / \
* 18 27 12 33
*
* Compare 55 with parent 41 → swap:
*
* Step 2: [55, 39, 41, 18, 27, 12, 33] (valid max-heap)
*
* Bubble up stops when the parent is already >= element or we reach the root.
*
* @param {number} index - Index of the node to bubble up (e.g. last after insert).
*/
bubbleUp(index) {
let i = index;
while (i > 0) {
const parent = Math.floor((i - 1) / 2);
if (this.values[parent] >= this.values[i]) break;
swap(this.values, parent, i);
i = parent;
}
}
/**
* Recursive bubble-up: move the node up while it is larger than its parent.
* This version is mainly for learning the tree-recursion viewpoint.
*
* @param {number} index - Index of the node to bubble up.
*/
bubbleUpRecursive(index) {
if (index === 0) return; // at root
const parent = Math.floor((index - 1) / 2);
if (this.values[parent] >= this.values[index]) return;
swap(this.values, parent, index);
this.bubbleUpRecursive(parent);
}
/**
* Remove and return the maximum (root). Place last element at root, then sink down.
*
* @example
* // Real-world: "Run the highest-priority task" — extractMax gives it and restores heap.
* const heap = new MaxBinaryHeap();
* [10, 30, 20].forEach(v => heap.insert(v));
* heap.extractMax(); // 30
*
* @returns {number | undefined} Maximum value, or undefined if heap is empty.
*
* Time: O(log n). Space: O(1).
*/
extractMax() {
if (this.values.length === 0) return undefined;
const max = this.values[0];
const end = this.values.pop();
if (this.values.length > 0 && end !== undefined) {
this.values[0] = end;
// Default: iterative sink-down (interview-friendly).
this.sinkDown(0);
}
return max;
}
/**
* Sink down the element at `index` until both children are <= it (restore max-heap).
* Swap with the larger child so the larger value moves up.
*
* Intuition: think of "gravity for small values" — a too-small value at the
* top will get pushed down while bigger values rise above it.
*
* @param {number} index - Index of the node to sink down (e.g. 0 after replacing root).
*
* Time: O(log n). Space: O(1).
*/
sinkDown(index) {
let idx = index;
const length = this.values.length;
const element = this.values[idx];
while (true) {
const leftChildIdx = 2 * idx + 1;
const rightChildIdx = 2 * idx + 2;
let leftChild, rightChild;
let swapIdx = null;
if (leftChildIdx < length) {
leftChild = this.values[leftChildIdx];
if (leftChild > element) {
swapIdx = leftChildIdx;
}
}
if (rightChildIdx < length) {
rightChild = this.values[rightChildIdx];
if (
(swapIdx === null && rightChild > element) ||
(swapIdx !== null && rightChild > leftChild)
) {
swapIdx = rightChildIdx;
}
}
if (swapIdx === null) break;
swap(this.values, idx, swapIdx);
idx = swapIdx;
}
}
/**
* Recursive sink-down: at each step, choose the larger child and recurse
* on that subtree if the child is larger than the current node.
*
* @param {number} index - Index of the node to sink down.
*/
sinkDownRecursive(index) {
const length = this.values.length;
const leftChildIdx = 2 * index + 1;
const rightChildIdx = 2 * index + 2;
let largestIdx = index;
if (
leftChildIdx < length &&
this.values[leftChildIdx] > this.values[largestIdx]
) {
largestIdx = leftChildIdx;
}
if (
rightChildIdx < length &&
this.values[rightChildIdx] > this.values[largestIdx]
) {
largestIdx = rightChildIdx;
}
if (largestIdx === index) return; // heap property satisfied
swap(this.values, index, largestIdx);
this.sinkDownRecursive(largestIdx);
}
}
// ============================================================================
// Example / sanity check
// ============================================================================
function checkHeap() {
const heap = new MaxBinaryHeap();
heap.insert(41);
heap.insert(39);
heap.insert(33);
heap.insert(18);
heap.insert(27);
heap.insert(12);
heap.insert(55);
console.log("Max-heap after inserts (array view):", heap.values);
console.log("extractMax():", heap.extractMax());
console.log("After extractMax:", heap.values);
// Demonstrate recursive versions explicitly (for learning).
const heap2 = new MaxBinaryHeap();
[41, 39, 33, 18, 27, 12, 55].forEach((val) => {
heap2.values.push(val);
heap2.bubbleUpRecursive(heap2.values.length - 1);
});
console.log("Max-heap built using recursive bubbleUp:", heap2.values);
heap2.sinkDownRecursive(0);
console.log("After one recursive sinkDown from root:", heap2.values);
}
checkHeap();
// ============================================================================
// Interview notes (for quick revision)
// ============================================================================
/**
* INTERVIEW CHECKLIST: Max / Min Binary Heaps
*
* 1. Core definitions:
* - Max-heap: parent >= children; root is the largest element.
* - Min-heap: parent <= children; root is the smallest element.
* - Stored as a complete binary tree inside an array.
*
* 2. Index formulas (very common question):
* - parent(i) = floor((i - 1) / 2)
* - left(i) = 2 * i + 1
* - right(i) = 2 * i + 2
*
* 3. Key operations & complexities:
* - Insert: O(log n) — put at end, bubble up.
* - Extract max/min: O(log n) — move last to root, sink down.
* - Peek max/min: O(1).
*
* 4. Typical interview questions where max-heap is useful:
* - Find K largest elements in an array.
* - Kth largest element.
* - "Stream of scores": maintain top K scores seen so far.
* - Scheduling / choosing next highest-priority job.
*
* 5. Common follow-up questions:
* - Heap vs. sorted array vs. balanced BST for priority queues.
* - Why are heaps good for repeated insert + extractMax/Min?
* - Heap sort algorithm and its time/space trade-offs.
* - How would you implement a generic heap that works with a comparator?
*
* NEXT STEPS / PRACTICE PROBLEMS:
* - Implement:
* - Kth largest / smallest element using max-heap or min-heap.
* - Stream "top K" tracker using a heap.
* - Priority queue that schedules tasks by deadline or priority.
* - Heap sort using MaxBinaryHeap.
* - For deeper understanding:
* - Draw the tree for a few heap arrays and manually simulate bubble-up and
* sink-down operations.
* - Implement both min-heap and max-heap and switch between them by only
* changing comparison operators.
*/
module.exports = MaxBinaryHeap;