给你一个整数 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 通常被视为丑数。
丑数 就是只包含质因数 2、3 和/或 5 的正整数。这又是一道堆的题,这道题我们可以用小顶堆去实现。
初始时堆为空。首先将最小的丑数 1 加入堆。
每次取出堆顶元素,该元素为最小丑数,则 也是丑数。将其入堆。
再取出堆顶,循环n次,此时堆顶即为第n小的丑数。
小顶堆在之前已有实现
var nthUglyNumber = function (n) {
const factors = [2, 3, 5];
const seen = new Set();
const heap = new Heap("small");
seen.add(1);
heap.push(1);
let ugly = 0;
for (let i = 1; i < n; i++) {
ugly = heap.pop();
for (const factor of factors) {
const next = ugly * factor;
if (!seen.has(next)) {
seen.add(next);
heap.push(next);
}
}
}
ugly = heap.pop();
return ugly;
};
小顶堆
class Heap {
constructor(cmp = "large") {
if (cmp == "large") {
this.cmp = this.large;
} else if (cmp == "small") {
this.cmp = this.small
} else {
this.cmp = cmp
}
this.res = [];
this.cnt = 0;
}
push (val) {
this.cnt++;
this.res.push(val)
this.shiftUp(this.cnt - 1)
}
pop () {
this.cnt--;
const res = this.res[0]
const pop = this.res.pop()
if (this.cnt) {
this.res[0] = pop
this.shiftDown(0)
}
return res
}
shiftUp (i) {
if (i === 0) return
const par = this.getParentIndex(i)
if (this.cmp(this.res[par], this.res[i])) {
this.swap(par, i)
this.shiftUp(par)
}
}
shiftDown (i) {
const l = this.getLeftIndex(i)
const r = this.getRightIndex(i)
if (l < this.cnt && this.cmp(this.res[i], this.res[l])) {
this.swap(i, l)
this.shiftDown(l)
}
if (r < this.cnt && this.cmp(this.res[i], this.res[r])) {
this.swap(i, r)
this.shiftDown(r)
}
}
getParentIndex (i) {
return (i - 1) >> 1
}
getLeftIndex (i) {
return i * 2 + 1
}
getRightIndex (i) {
return i * 2 + 2
}
large = (a, b) => a < b
small = (a, b) => a > b;
swap = (i, j) => [this.res[i], this.res[j]] = [this.res[j], this.res[i]];
top = () => this.res[0];
size = () => this.cnt;
isEmpty = () => this.cnt === 0
}
动态规划
这道题除了使用堆的方法,也可以使用动态规划,也就是三指针的方法
我们先将三个指针都指向1,p2 = 1, p3 = 1, p5 = 1;
我们创建一个数组dp长度为n+1。
将dp[1]=1
我们循环n次,每一次将三个指针进行对比num2 = dp[p2] * 2, num3 = dp[p3] * 3, num5 = dp[p5]取出最小值dp[i] = Math.min(Math.min(num2, num3), num5);
将其加入当前位置,取出最小值的指针移动一位。
var nthUglyNumber = function(n) {
const dp = new Array(n + 1).fill(0);
dp[1] = 1;
let p2 = 1, p3 = 1, p5 = 1;
for (let i = 2; i <= n; i++) {
const num2 = dp[p2] * 2, num3 = dp[p3] * 3, num5 = dp[p5] * 5;
dp[i] = Math.min(Math.min(num2, num3), num5);
if (dp[i] === num2) {
p2++;
}
if (dp[i] === num3) {
p3++;
}
if (dp[i] === num5) {
p5++;
}
}
return dp[n];
};