Nameless Site

But one day, you will stand before its decrepit gate,without really knowing why.

0%

丑数II

来自Leetcode第264题丑数II

编写一个程序,找出第 n 个丑数。

丑数就是只包含质因数 2, 3, 5正整数

示例:

1
2
3
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

来自官方题解

从堆中包含一个数字开始:1,去计算下一个丑数。将 1 从堆中弹出然后将三个数字添加到堆中:1 * 2,1 * 3和 1 * 5。

现在堆中最小的数字是 2。为了计算下一个丑数,要将 2 从堆中弹出然后添加三个数字:2×2, 2×3,和 2×5。

重复该步骤计算所有丑数。在每个步骤中,弹出堆中最小的丑数 kk,并在堆中添加三个丑数:k×2, k×3,和 k×5。

  • 初始化预计算用到的数组 nums,堆 heap 和哈希表 seen 跟踪在堆中出现过的元素,避免重复。
    • 循环计算丑数,每个步骤:
      • 弹出堆中最小的数字 k 并添加到数组 nums 中。
      • 2k3k5k 不存在在哈希表中,则将其添加到栈中并更新哈希表。
  • 返回在数组中预先计算好的丑数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Ugly {
public int[] nums = new int[1690];
Ugly() {
HashSet<Long> seen = new HashSet();
PriorityQueue<Long> heap = new PriorityQueue<Long>();
seen.add(1L);
heap.add(1L);

long currUgly, newUgly;
int[] primes = new int[]{2, 3, 5};
for(int i = 0; i < 1690; ++i) {
currUgly = heap.poll();
nums[i] = (int)currUgly;
for(int j : primes) {
newUgly = currUgly * j;
if (!seen.contains(newUgly)) {
seen.add(newUgly);
heap.add(newUgly);
}
}
}
}
}

class Solution {
public static Ugly u = new Ugly();
public int nthUglyNumber(int n) {
return u.nums[n - 1];
}
}

三指针

从数组中只包含一个丑数数字 1 开始,使用三个指针 i_2i2, i_3i3 和 i_5i5,标记所指向丑数要乘以的因子。

算法很简单:在 $2 \times \textrm{nums}[i_2],3 \times \textrm{nums}[i_3] 和 5 \times \textrm{nums}[i_5]$ 选出最小的丑数并添加到数组中。并将该丑数对应的因子指针往前走一步。重复该步骤直到计算完 1690 个丑数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Ugly {
public int[] nums = new int[1690];
Ugly() {
nums[0] = 1;
int ugly, i2 = 0, i3 = 0, i5 = 0;

for(int i = 1; i < 1690; ++i) {
ugly = Math.min(Math.min(nums[i2] * 2, nums[i3] * 3), nums[i5] * 5);
nums[i] = ugly;

if (ugly == nums[i2] * 2) ++i2;
if (ugly == nums[i3] * 3) ++i3;
if (ugly == nums[i5] * 5) ++i5;
}
}
}

class Solution {
public static Ugly u = new Ugly();
public int nthUglyNumber(int n) {
return u.nums[n - 1];
}
}