这个题之前写过,先上上一次写的链接 # 面试题 17.09. 第 k 个数(18)
这个题我觉得难点就怎么求的 n个质数的数组,上一次第k个数,听的我一脸蒙蔽,只知道代码比较简单,也不知道,原理是什么,通过现在看题解,知道这种解体思路是三指针动态规划
然而这次我看官方题解js是用最小堆实现的,看的不是太明白,还是怎么实现n个质数的数组,然后这几天一直在写大顶堆小顶堆,就再撸一遍,然后之前看人家实现大小顶堆给堆里面传送一个表达式也明白了,大小顶堆的判断逻辑,不一样的重点就是判断根节点和左右子节点.
方法一 小顶堆
/*
* @lc app=leetcode.cn id=264 lang=javascript
*
* [264] 丑数 II
*/
// @lc code=start
/**
* @param {number} n
* @return {number}
*/
const defaultCmp = (x, y) => x < y;
const swap = (arr, i, j) => [arr[i], arr[j]] = [arr[j], arr[i]];
class Heap {
constructor(cmp = defaultCmp) {
this.container = [];
this.cmp = cmp;
}
// 从下往上 入堆操作
insert(data) {
const { container, cmp } = this;
container.push(data);
let index = this.size() - 1;
while (index) {
let parent = index - 1 >> 1;
// 如果当前元素不大于当前元素的根元素 就停止
if (!cmp(container[index], container[parent])) {
return;
}
// 否则 交换
swap(container, index, parent);
// 上移动
index = parent;
}
}
// 弹出堆顶元素操作
pop() {
const { container, cmp } = this;
if (!this.size()) {
return null;
}
// 先交换首位节点
swap(container, 0, this.size() - 1);
// 交换完以后 弹出最后一个 即是刚刚交换道末尾的 堆顶元素
const res = container.pop();
// 开始 从上往下比较重新排序 需要堆顶坐标一个变量index,和另外一个标量用来做对比叫做exchange
let index = 0;
let exchange = 2 * index + 1; // 先赋值做节点
let length = this.size();
// 只要不越界就开始循环
while (exchange < length) {
// 判断有右节点不越界 并且右子节点比左节点大 则继续交换
let right = 2 * index + 2;
if (right < length && cmp(container[right], container[exchange])) {
exchange = right;
}
// exhchage 不比 index 大 就终止 break
if (!cmp(container[exchange], container[index])) {
break;
}
// 交换exchange和index
swap(container, exchange, index);
// 重置index和exchange 为下一步做对比
index = exchange;
exchange = 2 * index + 1;
}
// 返回弹出的最大值 即堆顶元素
return res;
}
// 堆的个数
size() {
return this.container.length;
}
// 堆顶元素
peek() {
if (this.size()) {
return this.container[0];
}
return null;
}
}
var nthUglyNumber = function (n) {
const factor = [2, 3, 5];
// 创建最小堆
const minHeap = new Heap();
// 定义一个map 用来储存n个丑数
const map = new Set();
// 最小堆和map都增加1
minHeap.insert(1);
map.add(1);
let ugly;
// 双重循环找到n个丑数
for (let i = 0; i < n; i++) {
ugly = minHeap.pop();
for (const f of factor) {
// 下一个丑数
const nextUgly = f * ugly;
// 判断map中有没有 因为3*5和5*3值一样 加过都不加了
if (!map.has(nextUgly)) {
minHeap.insert(nextUgly);
map.add(nextUgly);
}
}
}
return ugly;
};
// @lc code=end
方法二 三指针
var nthUglyNumber = function (n) {
let arr = [1], p2 = 0, p3 = 0, p5 = 0;
while (n) {
let min = Math.min(arr[p2] * 2, arr[p3] * 3, arr[p5] * 5);
arr.push(min);
if (arr[p2] * 2 === min) {
p2++;
}
if (arr[p3] * 3 === min) {
p3++;
}
if (arr[p5] * 5 === min) {
p5++;
}
n--;
}
// arr 多了第一个1 所以所以多减少一个
return arr[arr.length - 2];
};