多路归并技巧总结

264. 丑数 II

313. 超级丑数

373. 查找和最小的 K 对数字

786. 第 K 个最小的素数分数

1508. 子数组和排序后的区间和

719. 找出第 K 小的数对距离

1439. 有序矩阵中的第 k 个最小数组和

 

「多路归并」的核心思想与合并n个有序链表极其相似,题目详情可见 合并两个有序链表 合并K个升序链表

n个有序链表的头节点加入小根堆的优先队列中,由于n个链表都是递增的顺序,所以第一个最小的元素肯定在这n个头节点中产生

选择最小元素后,将该最小元素所在的链表后移一位,并将后一位元素加入队列中,以此类推...

1

而我们今天要介绍的「多路归并」本质就是上面的思想,唯一不同的就是,n个有序链表需要我们根据题目意思抽象出来而已,下面我们根据题目来依次分析!!

丑数 II

题目详情可见 丑数 II

对于一个丑数n,均可以衍生出三个与之对应的丑数:n * 2, n * 3, n * 5

3

这个题目的有序链表需要通过求得的丑数来动态获取,所以我们利用三个指针P1, P2, P3分别指向正在被处理的丑数

注意:需要去除相同元素。如上图第四小部分,第一条链表和第二条链表的当前值均为6,如果选择把第一条链表中的6加入ans中,那么第二条链条需要向前移动一格,否则6就加入了两次

下面给出具体具体代码:

超级丑数

题目详情可见 超级丑数

这个题目其实和上面的题目大同小异,无非就是质因数从 3 个衍生成更多个而已!

下面给出具体具体代码:

查找和最小的 K 对数字

题目详情可见 查找和最小的 K 对数字

对于例子:nums1 = [1,7,11], nums2 = [2,4,6],我们可以构造出三条递增的有序链表,如下图所示:

4

我们再来分析一下时间复杂度,假设n = nums1.length, m = num2.length

按照上图的方法构造有序链表的话,每次需要从n个元素中找出最小的元素,需要找k次,所以时间复杂度为O(klogn)

所以为了更优的时间复杂度,尽量让nums1长度更短;如果nums1长度更长,我们就交换两个数组的位置

下面给出具体具体代码:

第 K 个最小的素数分数

题目详情可见 第 K 个最小的素数分数

对于例子:arr = [1,2,3,5],我们可以构造出三条递增的有序链表

如下图所示:

5

这里有一个小技巧:如果直接比较除完之后的结果,可能会存在误差,所以可以来一波曲线救国

对于 ab<cd,我们只需要比较 ad<bc 即可

下面给出具体具体代码:

子数组和排序后的区间和

题目详情可见 子数组和排序后的区间和

对于例子:nums = [1,2,3,4],我们可以构造出四条递增的有序链表

如下图所示:

6

下面给出具体具体代码:

找出第 K 小的数对距离

题目详情可见 找出第 K 小的数对距离

对于例子:nums = [1,3,1],我们可以构造出两条递增的有序链表 (不过需要提前对数组排序,排序后的数组为[1,1,3])

下面给出具体具体代码:

不过很遗憾,这个题目用「多路归并」直接超时,这里给出这种方法只是为了加深对「多路归并」的理解!!

这个题目最佳的方法是「二分➕双指针」,详情可见 二分搜索:第 K 小问题

有序矩阵中的第 k 个最小数组和

题目详情可见 有序矩阵中的第 k 个最小数组和

这个题目稍微有一丢丢的不一样,对于每次弹出队顶元素后,需要把n个元素入队,如下图所示:

9

下面给出具体具体代码:

对于这个题目,这种方法效率并不是很高,这里给出这种方法只是为了加深对「多路归并」的理解!!

这个题目最佳的方法是「二分➕DFS」,详情可见 二分搜索:第 K 小问题