[路飞]_LeetCode题264丑数 II

141 阅读3分钟

题目描述

给你一个整数 n ,请你找出并返回第 n 个 丑数 。

丑数 就是只包含质因数 2、3 和/或 5 的正整数。

 

示例 1:

输入:n = 10 输出:12 解释:[1, 2, 3, 4, 5, 6, 8, 9, 10, 12] 是由前 10 个丑数组成的序列。 示例 2:

输入:n = 1 输出:1 解释:1 通常被视为丑数。  

提示:

1 <= n <= 1690

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ug… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解思路

  1. 任何一个丑数都是由==>丑数=2k2^k3k3^k5k5^k,k是非负整数。
  2. 最小的丑数应该是202^0303^0505^0=1。
  3. 下一个丑数应该是由之前的丑数分别与2、3、5相乘取其中最小的结果。
  4. 如我们已知第一位为1,那么第2位应该为第一位(之前的丑数)分别与2、3、5相乘取其中最小的结果。应为1×2=2。
  5. 以此类推,第三位应该是前两位分别与2、3、5相乘取其中最小的结果,,1×3,1×5,2×2,2×3,2×5中最小的数即1×3,因为1×2是第二个丑数,所以在比较第三个丑数时,不应该再次把已知的结果添加到比较中,所以为了达到这个目的,我们使用3个指针分别指向2、3、5分别与已知丑数队列中相乘的下一个位置。
  6. 这个意思就是初始时三个指针都是p2=p3=p5=1,表示下一个数由2、3、5分别去乘以自己对应的指针位置的数做最小比较,哪个数最小,那么相应的指针就+1,1×2最小,说明2已经和第一位丑数计算过并计入结果,下次比较的时候,2这个数就从第二个丑数位开始相乘即可。
  7. 对于重复的数,如2×3和3×2,当p2指针指向3,p3指针指向2,此时我们比较出的最小数是6,满足2×3和3×2,所以此时我们将p2和p3的指针同时+1,这样就避免了重复的现象。

题解代码

 * @lc app=leetcode.cn id=264 lang=javascript
 *
 * [264] 丑数 II
 */

// @lc code=start
/**
 * @param {number} n
 * @return {number}
 */
var nthUglyNumber = function(n) {
  //下一个丑数都是由之前的丑数分别乘以2、3、5取最小的那一个
  //比如1的下一个丑数应该是1*2,1*3,1*5中最小的那个是2
  //所以丑数前两个变成了[1,2]
  //第三个丑数是由1*2,1*3,1*5以及2*2,2*3,2*5中最小的那个,即1*3=3,
  //而这次计算比较中由于1*2的结果已经计入丑数队列中,所以这次计算无需再比较1*2的结果
  //所以我们通过定义三个指针p2,p3,p5分别表示2,3,5分别与之前第几位的丑数相乘过的结果已计入丑数队列中的下一个位置
  //如p2=1时,我们比较的数中会有p2*2=1*2=2,发现这个数是目前最小值,那么说明2已经和第p2=1的位置的数乘过了,结果已经计入
  //所以在计算下一个数的时候,2不需要乘以丑数队列的1号位,所以p2的值此时应该+1
  //以此类推3和5
  let p2 = 1,p3 = 1,p5 = 1;
  const ans = new Array(n+1);

  ans[1] = 1;//最小丑数为1,也是第一个丑数
  for (let i = 2; i <= n; i++) {
    let m2 = ans[p2]*2;
    let m3 = ans[p3]*3;
    let m5 = ans[p5]*5;
    let min = Math.min(m2,m3,m5);
    console.log(m2,m3,m5,min)
    if (min === m2) p2++;
    if (min === m3) p3++;
    if (min === m5) p5++;
    ans[i] = min;
  }
  return ans[n];
};
// @lc code=end