前端就该用 JS 刷算法29(补)

715 阅读1分钟

每日一题 -- 堆

313. 超级丑数(补28号)

313. 超级丑数

分析

  1. 只要建好了堆,只需要暴露出堆和入堆的 api,其他的理解起来还是很简单的
  2. 先将 prices 都放入最小堆里,堆里的都是丑数
  3. 每一次出堆一个最小的丑数,同时记录这是第 i 个丑数
  4. 然后用这个最小的丑数去乘 prices 中的值,去重后(所有出现过的丑数都要去重),加入到小顶堆中
  5. 重复第四部,一直到出堆的是第 n 个超级丑数即可
// https://leetcode-cn.com/problems/super-ugly-number/
// 313. 超级丑数

/**
 * @分析 -- 超时了
 * 
 * @分析2 
 * 1. 其实也是用堆,只是插入之后,可以直接 up 上去,
 * 2. 然后直接封装好 heappush 和 heappop,然后还是很好理解的。
 * 3. 每次都从小顶堆中取最小值,然后和 primes 相乘,然后去重后插入到小顶堆中
 * 4. 取出的就是第 1,2,3,...n 个
 * 
 */
var nthSuperUglyNumber = function (n, primes) {
    const len = primes.length
    // 构建初始化的小顶堆
    const minHeap = new Heap
    let map = new Map()
    minHeap.heappush(1)
    let count = 0
    let res = 1
    for(let price of primes){
        minHeap.heappush(price)
        map.set(price, 1)
    }
    while (count < n) {
        res = minHeap.heappop()
        for(let price of primes){
            const temp = price * res
            if (!map.has(temp)) {
                minHeap.heappush(temp)
                map.set(temp, 1)
            }
        }
        count++
    }

    // 第 n 次循环的时候已经跳出,所以 res 取到的是 n-1 次循环的时的值,
    // 但是由于第一个值是1,所以 res 就是第 n 个超级丑数
    return res
};

// 构建小顶堆
const Heap = function () {
    this.data = []
}

Heap.prototype.heappush = function (val) {
    // 必须先提取出来,不然往数组中追加之后,会导致数组边长
    // 再用 this.data.length 去 up 就会失败了
    const len = this.data.length
    this.data[len] = val
    this.up(len)
}
Heap.prototype.heappop = function () {
    // 堆顶和最后一个值交换
    this.swap(0, this.data.length - 1)
    // 删除掉堆顶的值
    const res = this.data.pop()
    // 自上而下的整理
    this.down(0)
    return res
}

Heap.prototype.swap = function (a, b) {
    [this.data[a], this.data[b]] = [this.data[b], this.data[a]]
}


Heap.prototype.down = function (index) {
    if (index >= this.data.length) return
    const left = index * 2 + 1
    const right = index * 2 + 2
    let target = index
    if (left < this.data.length && this.data[left] < this.data[target]) {
        target = left
    }
    if (right < this.data.length && this.data[right] < this.data[target]) {
        target = right
    }
    if (target !== index) {
        this.swap(target, index)
        // 整理子节点
        this.down(target)
    }
}

Heap.prototype.up = function (index) {
    if (index === 0) return
    const father = (index - 1) >>> 1
    if (this.data[index] < this.data[father]) {
        this.swap(index, father)
        this.up(father)
    }
}