面试题49:丑数(C++)
题目地址:https://leetcode-cn.com/problems/chou-shu-lcof/
题目描述
我们把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
题目示例
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12
是前 10 个丑数。
解题思路
首先分析何谓丑数?一个数的因子只包含2,3,5的数称为丑数。数字1特别对待也看作是丑数,所以从1开始的10个丑数分别为1,2,3,4,5,6,8,9,10,12。那么,如何判断一个数是不是丑数呢?我们使用循环冗余法判断,即设待判断整数位N,N循环除以2直到不能整除,此时接着循环除以3直到不能整除,接着循环除以5直到商为1或者不能整除为止。商为1且余数为0则为丑数,否则为非丑数。以12为例,循环除以2,12/2=6,6/2=3,3/2不能整除,转为循环除以3,3/3=1,结束,12为丑数。我们可以发现除了1以外,丑数都是由某个丑数*2或者*3或者*5得到的。如2是丑数1*2得到的,3是丑数1*3得到的,4是丑数1*4得到的,5是丑数1*5得到的,6是丑数2*3得到的……。所以,我们使用动态规划的思想解决。
动态规划:我们设动态规划列表为dp,初始化第一个丑数dp[0]=1,其中dp[i]代表第i+1个丑数,状态转移方程为dp[i]=min(dp[a]*2,min(dp[b]*3,dp[c]*5),最后返回第n个丑数dp[n-1]。每轮计算dp[i]之后,需要更新a、b、c的值,分别独立判断dp[i]与dp[a]*2、dp[b]*3、dp[c]*5的大小是否相等,若相等,则对应的索引a、b、c自增一。
队列:我们使用三个队列q2、q3、q5分别存放当前访问值乘2、乘3、乘5的值,同时在迭代的过程里取出三个队列里最小的作为下轮访问的值,需要注意的是,取出数字的时候需要更新可能的值,为了避免重复插入的情况,比如q2插入了6,q3也插入了6,我们只允许从q2中取出的值乘2、乘3、乘5,q3中取出的值只允许乘3或乘5,q5中取出的值只允许乘5
程序源码
动态规划
class Solution { public: int nthUglyNumber(int n) { if(n == 1) return 1; int a = 0, b = 0, c = 0; vector<int> dp(n); dp[0] = 1; for(int i = 1; i < n; i++) { dp[i] = min(dp[a] * 2, min(dp[b] * 3, dp[c] * 5)); if(dp[i] == dp[a] * 2) a++; if(dp[i] == dp[b] * 3) b++; if(dp[i] == dp[c] * 5) c++; } return dp[n - 1]; } };
队列
class Solution { public: int nthUglyNumber(int n) { if(n == 1) return 1; queue<long> q2, q3, q5; q2.push(2); q3.push(3); q5.push(5); long res; for(int i = 1; i < n; i++) { long tmp_2 = q2.front(); long tmp_3 = q3.front(); long tmp_5 = q5.front(); res = min(min(tmp_2, tmp_3),tmp_5); if(res == tmp_2) { q2.pop(); q2.push(res * 2); q3.push(res * 3); q5.push(res * 5); } if(res == tmp_3) { q3.pop(); q3.push(res * 3); q5.push(res * 5); } if(res == tmp_5) { q5.pop(); q5.push(res * 5); } } return res; } };
参考文章