笔者在刷题的过程中,多次看到多路归并的身影,因此尝试对此做个专题。多路归并就是将多个序列进行归并,产生的结果序列中有我们想要的结果,但是无脑归并势必会产生一些后果,比如多路中有重复等等,因此就是需要一些算法技巧来处理。
多路归并最经典的题目应该是“丑数”,因此我从这一题开始。
相关题目
264.丑数2
参考宫水三叶的题解,从优先队列找最小值,复杂度为O(NlogN)
,优化到了多路归并多指针移动找最小值,复杂度为O(N)
。用dp[i]
记录结果,本质上也是动态规划,每个序列的当前值是根据其目前在dp[i]指向的最小值dp[p]决定的(这和以前我们写的只有一个指针的动态规划不一样),然后当前的最小值dp[i]是所有序列指向最小值dp[p]的下一个的最小值。同时在移动比对minV==a或者minV==b或者minV=c
过程中,不仅移动了每个序列的指针,也排除了多个序列中会重复选取一个值的可能。
class Solution {
public int nthUglyNumber(int n) {
int[] dp=new int[n+1];
dp[1]=1;
//定义三个指针
int p1=1;
int p2=1;
int p3=1;
for(int i=2;i<=n;i++){
int a=2*dp[p1],b=3*dp[p2],c=5*dp[p3];
int res=Math.min(a,Math.min(b,c));
dp[i]=res;
if(a==res) p1++;
if(b==res) p2++;
if(c==res) p3++;
}
return dp[n];
}
}
第k个数
同丑数2
class Solution {
public int getKthMagicNumber(int k) {
long[]dp=new long[k+1];
dp[1]=1;
int dp1=1;
int dp2=1;
int dp3=1;
for(int i=2;i<=k;i++){
long a= 3L*dp[dp1],b=5L*dp[dp2],c=7L*dp[dp3];
long res=Math.min(a,Math.min(b,c));
dp[i]=res;
if(a==res) dp1++;
if(b==res) dp2++;
if(c==res) dp3++;
}
return (int)dp[k];
}
}