剑指 Offer 49. 丑数
思路一:
利用小根堆。
小根堆弹出的数是堆里最小的,于是弹出一个x。
就将2*x,3*x,5*x加入set去重,然后重新加入小根堆。
弹出的第n个数就是第n个丑数。
class Solution { public int nthUglyNumber(int n) { int[] factors = {2, 3, 5}; Queue<Long> heap = new PriorityQueue<>(); HashSet<Long> set = new HashSet<>(); heap.offer(1L); set.add(1L); int res = 0; for(int i = 0; i < n; i++){ long cur = heap.poll(); //记录当前弹出的数 res = (int)cur; //弹一个数x,将2*x,3*x,5*x,先放入set去重,然后加入进小根堆 for(int nums : factors){ if(set.add(nums * cur)) heap.offer(nums * cur); } } return res; } }
思路二:动态规划
1.状态定义
dp[i]表示第i + 1个丑数
2.状态转移方程
dp[i]=min(dp[a]×2,dp[b]×3,dp[c]×5)
class Solution { public int nthUglyNumber(int n) { int[] dp = new int[n+1]; dp[0] = 1; int a = 0, b = 0, c = 0; //因为因子相同,丑数可以从之前的丑数得出 //下一个丑数必定是之前某个丑数*2或者*3或者*5得出 //于是从小到大遍历丑数,比较每个丑数*2,*3,*5的结果 //dp[i]=min(dp[a]×2,dp[b]×3,dp[c]×5) for (int i = 1; i < n; i++){ int num2 = dp[a] * 2, num3 = dp[b] * 3, num5 = dp[c] * 5; dp[i] = Math.min(Math.min(num2, num3), num5); //可能会有多个指针同时向后退 //因为num2可能等于num3类似的 if(dp[i] == num2) a++; if(dp[i] == num3) b++; if(dp[i] == num5) c++; } //dp[i]表示第i + 1个丑数的值,因为dp[0]是第一个丑数为1 return dp[n - 1]; } }