携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
丑数
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。1是丑数,n不超过1600
来源:力扣(LeetCode) 链接:leetcode.cn/problems/ch…
分析
- 在看过上一篇文章后,看到这个题目,能比较容易的想到小顶堆
最小堆
- 从小到大第n个,有序,前n个,小顶堆
- 1是最小的丑数,为了避免重复,使用set,每次拿堆顶的数去和2,3,5相乘,得到新的丑数
- 麻烦的地方在于要自己实现一个小顶堆
代码
var nthUglyNumber = function(n) {
const factors = [2, 3, 5];
const seen = new Set();
const heap = new MinHeap();
seen.add(1);
heap.insert(1);
let ugly = 0;
for (let i = 0; i < n; i++) {
ugly = heap.pop();
for (const factor of factors) {
const next = ugly * factor;
if (!seen.has(next)) {
seen.add(next);
heap.insert(next);
}
}
}
return ugly;
};
class MinHeap {
constructor() {
this.heap = [];
}
getParentIndex(i) {
return (i - 1) >> 1;
}
getLeftIndex(i) {
return i * 2 + 1;
}
getRightIndex(i) {
return i * 2 + 2;
}
shiftUp(index) {
if(index === 0) { return; }
const parentIndex = this.getParentIndex(index);
if(this.heap[parentIndex] > this.heap[index]){
this.swap(parentIndex, index);
this.shiftUp(parentIndex);
}
}
swap(i1, i2) {
const temp = this.heap[i1];
this.heap[i1]= this.heap[i2];
this.heap[i2] = temp;
}
insert(value) {
this.heap.push(value);
this.shiftUp(this.heap.length - 1);
}
pop() {
this.heap[0] = this.heap.pop();
this.shiftDown(0);
return this.heap[0];
}
shiftDown(index) {
const leftIndex = this.getLeftIndex(index);
const rightIndex = this.getRightIndex(index);
if (this.heap[leftIndex] < this.heap[index]) {
this.swap(leftIndex, index);
this.shiftDown(leftIndex);
}
if (this.heap[rightIndex] < this.heap[index]){
this.swap(rightIndex, index);
this.shiftDown(rightIndex);
}
}
peek() {
return this.heap[0];
}
size() {
return this.heap.length;
}
}
动态规划
- 在使用小顶堆的时候,会算额外的丑数,而且实现一个小顶堆也很麻烦,使用动态规划优化
- 丑数数组第一个是1,当前因子2、3、5都指向第一个丑数,然后算下一个丑数的时候,这三个因子与所指的数字相乘,最小的那个就是下一个丑数,哪个因子乘出了最小值,这个因子对应的指针向后移动一位
代码
var nthUglyNumber = function(n){
const dp = [0]
dp[1] = 1
let p2 = 1, p3 = 1, p5 = 1
for(let i=2;i<=n;i++){
let num2 = dp[p2]*2
let num3 = dp[p3]*3
let 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]
}
总结
- 本题难度等级为中等,可以比较容易相处小顶堆和动态规划的方法
- 比起小顶堆,还是更推荐动态规划,个人还是喜欢需要哪个数算到哪个数
- 今天也是有收获的一天