Skip to content

Commit 5b9a008

Browse files
committed
update
1 parent f20f818 commit 5b9a008

File tree

3 files changed

+326
-49
lines changed

3 files changed

+326
-49
lines changed

images/unbounded.png

182 KB
Loading

✅ Pattern 14: K-way merge.md

+97-44
Original file line numberDiff line numberDiff line change
@@ -291,90 +291,127 @@ console.log(
291291
for counting, therefore, the overall time complexity of the algorithm will be `O(N*log(max-min))`.
292292
- The algorithm runs in constant space `O(1)`.
293293

294-
## Smallest Number Range (Hard)
294+
## 📍 Smallest Number Range (Hard)
295295

296296
https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/
297297

298298
> Given `M` sorted arrays, find the smallest range that includes at least one number from each of the `M` lists.
299299
300300
This problem follows the [K-way merge pattern](#pattern-14-k-way-merge) and we can follow a similar approach as discussed in [Merge K Sorted Lists](#👩🏽‍🦯-🌴-merge-k-sorted-arrays).
301301

302-
We can start by inserting the first number from all the arrays in a `min-heap()`. We will keep track of the largest number that we have inserted in the heap (let’s call it `currentMax`).
302+
We can start by inserting the first number from all the arrays in a `min-heap()`. We will keep track of the largest number that we have inserted in the heap (let’s call it `maxNumber`).
303303

304-
In a loop, we’ll take the smallest (top) element from the `min-heap()` and `currentMax` has the largest element that we inserted in the heap. If these two numbers give us a smaller range, we’ll update our range. Finally, if the array of the top element has more elements, we’ll insert the next element to the heap.
304+
In a loop, we’ll take the smallest (top) element from the `min-heap()` and `maxNumber` has the largest element that we inserted in the heap. If these two numbers give us a smaller range, we’ll update our range. Finally, if the array of the top element has more elements, we’ll insert the next element to the heap.
305305

306306
We can finish searching the minimum range as soon as an array is completed or, in other terms, the <b>heap</b> has less than `M` elements.
307307

308308
```js
309-
class Heap {
309+
class MinHeap {
310310
constructor() {
311-
this.arr = [];
311+
this.list = []; // a list of [num, groupID]
312+
this.size = 0;
312313
}
313314

314-
size() {
315-
return this.arr.length;
316-
}
315+
push(item) {
316+
const list = this.list;
317+
const size = ++this.size;
317318

318-
push(val) {
319-
this.arr.push(val);
320-
this.arr.sort((a, b) => a[0] - b[0]);
319+
list[size - 1] = item;
320+
this.bubbleUp(size - 1);
321321
}
322322

323323
pop() {
324-
return this.arr.shift();
324+
if (this.size === 0) return;
325+
326+
const list = this.list;
327+
const size = this.size;
328+
const item = list[0];
329+
330+
[list[0], list[size - 1]] = [list[size - 1], list[0]];
331+
this.size--;
332+
this.bubbleDown(0);
333+
return item;
334+
}
335+
336+
bubbleUp(index) {
337+
const list = this.list;
338+
const size = this.size;
339+
const parent = Math.floor((index - 1) / 2);
340+
341+
if (parent < 0 || parent >= size) return;
342+
if (index < 0 || index >= size) return;
343+
344+
if (list[index][0] < list[parent][0]) {
345+
[list[index], list[parent]] = [list[parent], list[index]];
346+
this.bubbleUp(parent);
347+
}
348+
}
349+
350+
bubbleDown(index) {
351+
if (index < 0 || index >= this.size) return;
352+
353+
const list = this.list;
354+
const size = this.size;
355+
const left = index * 2 + 1;
356+
const right = index * 2 + 2;
357+
let minVal = list[index][0];
358+
let minIndex = index;
359+
360+
if (left >= 0 && left < size) {
361+
if (list[left][0] < minVal) {
362+
minVal = list[left][0];
363+
minIndex = left;
364+
}
365+
}
366+
if (right >= 0 && right < size) {
367+
if (list[right][0] < minVal) {
368+
minVal = list[right][0];
369+
minIndex = right;
370+
}
371+
}
372+
if (minIndex !== index) {
373+
[list[index], list[minIndex]] = [list[minIndex], list[index]];
374+
this.bubbleDown(minIndex);
375+
}
325376
}
326377
}
327378

328-
function findSmallestRange(lists) {
329-
let minHeap = new Heap();
379+
function smallestRange(nums) {
380+
const minHeap = new MinHeap();
381+
const pointers = Array(nums.length).fill(0);
330382
let rangeStart = 0;
331383
let rangeEnd = Infinity;
332384
let maxNumber = -Infinity;
333385

334386
// put the first element of each array into the heap
335-
for (let num of lists) {
336-
minHeap.push([num[0], 0, num]);
337-
maxNumber = Math.max(maxNumber, num[0]);
387+
for (let i = 0; i < nums.length; i++) {
388+
minHeap.push([nums[i][0], i]);
389+
maxNumber = Math.max(maxNumber, nums[i][0]);
338390
}
339391

340392
// take the smallest(top) element from the heap, if it gives us smaller range, update the ranges
341393
// if the array of the top element has more elements, insert the next element in the heap
342-
while (minHeap.size() == lists.length) {
343-
let [num, i, list] = minHeap.pop();
394+
while (true) {
395+
let [minNumber, group] = minHeap.pop();
344396

345-
if (rangeEnd - rangeStart > maxNumber - num) {
346-
rangeStart = num;
397+
if (maxNumber - minNumber < rangeEnd - rangeStart) {
398+
rangeStart = minNumber;
347399
rangeEnd = maxNumber;
348400
}
349401

350-
if (list.length > i + 1) {
351-
// insert the next element into the heap
352-
minHeap.push([list[i + 1], i + 1, list]);
353-
maxNumber = Math.max(maxNumber, list[i + 1]);
354-
}
402+
pointers[group]++;
403+
if (pointers[group] >= nums[group].length) break;
404+
405+
// insert the next element into the heap
406+
minHeap.push([nums[group][pointers[group]], group]);
407+
maxNumber = Math.max(maxNumber, nums[group][pointers[group]]);
355408
}
356409

357410
return [rangeStart, rangeEnd];
358411
}
359412

360413
console.log(`Smallest range is:
361-
${findSmallestRange([
362-
[1, 5, 8],
363-
[4, 12],
364-
[7, 8, 10],
365-
])}`);
366-
// The range [4, 7] includes 5 from L1, 4 from L2 and 7 from L3.
367-
368-
console.log(`Smallest range is:
369-
${findSmallestRange([
370-
[1, 9],
371-
[4, 12],
372-
[7, 10, 16],
373-
])}`);
374-
// The range [9, 12] includes 9 from L1, 12 from L2 and 10 from L3.
375-
376-
console.log(`Smallest range is:
377-
${findSmallestRange([
414+
${smallestRange([
378415
[4, 10, 15, 24, 26],
379416
[0, 9, 12, 20],
380417
[5, 18, 22, 30],
@@ -385,14 +422,30 @@ ${findSmallestRange([
385422
// List 3: [5, 18, 22, 30], 22 is in range [20,24].
386423

387424
console.log(`Smallest range is:
388-
${findSmallestRange(
425+
${smallestRange(
389426
(nums = [
390427
[1, 2, 3],
391428
[1, 2, 3],
392429
[1, 2, 3],
393430
])
394431
)}`);
395432
// [1,1]
433+
434+
console.log(`Smallest range is:
435+
${smallestRange([
436+
[1, 5, 8],
437+
[4, 12],
438+
[7, 8, 10],
439+
])}`);
440+
// The range [4, 7] includes 5 from L1, 4 from L2 and 7 from L3.
441+
442+
console.log(`Smallest range is:
443+
${smallestRange([
444+
[1, 9],
445+
[4, 12],
446+
[7, 10, 16],
447+
])}`);
448+
// The range [9, 12] includes 9 from L1, 12 from L2 and 10 from L3.
396449
```
397450

398451
- Since, at most, we’ll be going through all the elements of all the arrays and will remove/add one element in the heap in each step, the time complexity of the above algorithm will be `O(N*logM)` where `N` is the total number of elements in all the `M` input arrays.

0 commit comments

Comments
 (0)