[路飞] 丑数 II

120 阅读1分钟

题目描述

给你一个整数 n ,请你找出并返回第 n 个 丑数 。

丑数 就是只包含质因数 23 和/或 5 的正整数。

分析

输入: Number, 表示要找到的第 n 个整数
输出: Number, 找到的丑数

解题思路

数据结构

本体用小顶堆来完成,思路是从堆中弹出 n 个丑数。

步骤

一开始,堆中只有 1 这么一个丑数,那么在弹出的时候,不断地去生成新的丑数,然后再往里放。

丑数的生成

每个丑数,都是其它丑数 * 2 / 3 / 5 得来的,但是我们在乘的时候需要注意,如果这个数的最大因子是 3,那么它就不要和 2 进行相乘了,因为会导致重复丑数的生成,这个原因是:

例如: 3 * 2 === 2 * 3,

所以我们只要保证,每个丑数不和比他的最大因子小的数字相乘,就可以了。

过程整理

  • 手写最小堆
  • 把 1 放入堆
  • 不断的取出最小丑数,生成新的丑数
  • 返回生成的第 n 个丑数

代码

/**
 * @param {number} n
 * @return {number}
 */
var nthUglyNumber = function (n) {
  const heap = new MinHeap((a, b) => a > b)
  heap.insert(1)
  let ans

  while (n--) {
    ans = heap.extract()

    if (ans % 5 === 0) {
      heap.insert(ans * 5)
    } else if (ans % 3 === 0) {
      heap.insert(ans * 5)
      heap.insert(ans * 3)
    } else {
      heap.insert(ans * 5)
      heap.insert(ans * 3)
      heap.insert(ans * 2)
    }
  }

  return ans
}

class MinHeap {
  constructor(compareFn) {
    this.heap = []
    this.compareFn = compareFn
  }

  getLeftIndex(index) {
    return index * 2 + 1
  }

  getRightIndex(index) {
    return index * 2 + 2
  }

  getParentIndex(index) {
    return Math.floor((index - 1) / 2)
  }

  size() {
    return this.heap.length
  }

  isEmpty() {
    return this.size() === 0
  }

  swap(parent, index) {
    const arr = this.heap

    ;[arr[parent], arr[index]] = [arr[index], arr[parent]]
  }

  insert(value) {
    const index = this.size()
    this.heap.push(value)
    this.siftUp(index)
  }

  siftUp(index) {
    let parent = this.getParentIndex(index)

    while (index > 0 && this.compareFn(this.heap[parent], this.heap[index])) {
      this.swap(parent, index)
      index = parent
      parent = this.getParentIndex(index)
    }
  }

  extract() {
    if (this.isEmpty()) return
    if (this.size() === 1) return this.heap.pop()

    const removedValue = this.heap[0]
    this.heap[0] = this.heap.pop()
    this.siftDown(0)

    return removedValue
  }

  siftDown(index) {
    let element = index
    const left = this.getLeftIndex(index)
    const right = this.getRightIndex(index)

    if (
      index < this.size() &&
      this.compareFn(this.heap[element], this.heap[left])
    ) {
      element = left
    }

    if (
      index < this.size() &&
      this.compareFn(this.heap[element], this.heap[right])
    ) {
      element = right
    }

    if (index !== element) {
      this.swap(element, index)
      this.siftDown(element)
    }
  }

  top() {
    return this.heap[0]
  }
}