LeetCode算法学习之--Heap--丑数

171 阅读2分钟

大家好今天给大家分享下一道 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