大家好今天给大家分享下一道 LeetCode 中等难度 的题目[剑指 Offer 49. 丑数]
题目
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
输入: n = 10 输出: 12 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
分析
1.丑数必须是 2 3 5的倍数
2.1 也是丑数
3.返回第n个抽数
解法
1.minHeap
2.暴力法
解法一:minHeap
思路
1.把 1放入最小堆中
2.迭代取出堆中的最小值
3.再用最小值 乘以2 3 5 放入最小堆中,因为可能有重复值所以需要使用set去重
4.当迭代的次数到达n次 则表明当前取出的堆中的最小值就是答案
*/
// 因为js中没有最小堆,所以需要手动写一个
class MinHeap {
constructor(arr) {
this.array = [];
if (Array.isArray(arr)) {
for (const item of arr) {
this.insert(item);
}
}
}
// 交换元素的方法
swap(arr, i, j) {
[arr[j], arr[i]] = [arr[i], arr[j]];
}
// 插入元素
insert(item) {
this.array.push(item);
let index = this.array.length - 1;
// 迭代 直到 最上面的最小
while (index) {
// 和父类比较
const parentIndex = Math.floor((index - 1) / 2);
if (this.array[parentIndex] < this.array[index]) {
break;
}
this.swap(this.array, index, parentIndex);
index = parentIndex;
}
}
poll() {
this.swap(this.array, 0, this.array.length - 1);
const res = this.array.pop();
let index = 0;
let changeIndex = index * 2 + 1;
// 迭代 直到 最上面的最小
while (changeIndex < this.array.length) {
const rightIndex = index * 2 + 2;
// 左右子元素比较 哪个小,小的哪个才去和index比较
if (
rightIndex < this.array.length &&
this.array[rightIndex] < this.array[changeIndex]
) {
changeIndex = rightIndex;
}
if (this.array[changeIndex] > this.array[index]) {
break;
}
this.swap(this.array, index, changeIndex);
index = changeIndex;
changeIndex = index * 2 + 1;
}
return res;
}
}
var nthUglyNumber = function (n) {
//创建minHeap
const minHeap = new MinHeap();
//创建set去重
const set = new Set();
//因子
const factors = [2, 3, 5];
minHeap.insert(1);
let res = -1;
// 迭代n次, 第n次就是答案
for (let i = 0; i < n; i++) {
res = minHeap.poll();
// 迭代因子,把 2 3 5 的倍数都放入minHeap中
factors.forEach((item) => {
const number = res * item;
if (!set.has(number)) {
minHeap.insert(number);
set.add(number);
}
});
}
return res;
};
/* 复杂度
时间 O(nlogn)
空间 O(n)
*/
解法二:暴力法
/*
思路
1.因为只能是2,3,5的倍数,所以可以把 2 3 5 分成3个组作为丑数元素池,
2.但是因为丑数的单调递增的,所以每次取出最小值放入丑数的数列中
3.使用最小值乘以每个数列的因子,然后扩充元素池
*/
var nthUglyNumber = function (n) {
// 初始化各个组
let list2 = [2];
let list3 = [3];
let list5 = [5];
const res = [1];
for (let i = 1; i < n; i++) {
// 取出最小值
const min = getMinNumber(list2, list3, list5);
res.push(min);
// 使用最小值乘以因子给每个组添加新的元素
list2 = addNewNumberToArr(2, min, list2);
list3 = addNewNumberToArr(3, min, list3);
list5 = addNewNumberToArr(5, min, list5);
}
return res[res.length - 1];
function getMinNumber(arr1, arr2, arr3) {
const min = Math.min(...arr1, ...arr2, ...arr3);
Array.from(arguments).forEach((item) => {
if (item[0] === min) {
item.shift();
}
});
return min;
}
function addNewNumberToArr(factor, min, arr) {
return [...arr, factor * min];
}
};
/* 复杂度
时间 O(n^2)
空间 O(n)
*/
总结
这道题考察的是minHeap的应用,如何使用minHeap来存取元素
大家可以看看我分享的一个专栏(前端搞算法)里面有更多关于算法的题目的分享,希望能够帮到大家,我会尽量保持每天晚上更新,如果喜欢的麻烦帮我点个赞,十分感谢
大家如果对“TS”感兴趣的可以看看我的专栏 (TypeScript常用知识),感谢大家的支持
文章内容目的在于学习讨论与分享学习算法过程中的心得体会,文中部分素材来源网络,如有侵权,请联系删除,邮箱 182450609@qq.com