@@ -6,9 +6,9 @@ Tag : 「多路归并」、「优先队列(堆)」
6
6
7
7
8
8
9
- 给你一个整数 n ,请你找出并返回第 n 个 丑数 。
9
+ 给你一个整数 ` n ` ,请你找出并返回第 ` n ` 个 丑数 。
10
10
11
- 丑数 就是只包含质因数 2、3 和 5 的正整数。
11
+ 丑数 就是只包含质因数 ` 2 ` 、 ` 3 ` 和 ` 5 ` 的正整数。
12
12
13
13
示例 1:
14
14
```
@@ -28,7 +28,7 @@ Tag : 「多路归并」、「优先队列(堆)」
28
28
```
29
29
30
30
提示:
31
- * 1 <= n <= 1690
31
+ * $ 1 <= n <= 1690$
32
32
33
33
---
34
34
@@ -41,7 +41,7 @@ Tag : 「多路归并」、「优先队列(堆)」
41
41
42
42
---
43
43
44
- ### 优先队列(小根堆)解法
44
+ ### 优先队列(小根堆)
45
45
46
46
有了基本的分析思路,一个简单的解法是使用优先队列:
47
47
@@ -52,8 +52,8 @@ Tag : 「多路归并」、「优先队列(堆)」
52
52
53
53
为了防止同一丑数多次进队,我们需要使用数据结构 $Set$ 来记录入过队列的丑数。
54
54
55
- 代码:
56
- ``` java
55
+ Java 代码:
56
+ ``` Java
57
57
class Solution {
58
58
int [] nums = new int []{2 ,3 ,5 };
59
59
public int nthUglyNumber (int n ) {
@@ -76,31 +76,83 @@ class Solution {
76
76
}
77
77
}
78
78
```
79
+ Python 代码:
80
+ ``` Python
81
+ class Solution (object ):
82
+ def nthUglyNumber (self , n ):
83
+ """
84
+ :type n: int
85
+ :rtype: int
86
+ """
87
+ nums = [2 ,3 ,5 ]
88
+ explored = {1 }
89
+ pq = [1 ]
90
+ for i in range (1 , n+ 1 ):
91
+ x = heapq.heappop(pq)
92
+ if i == n:
93
+ return x
94
+ for num in nums:
95
+ t = num * x
96
+ if t not in explored:
97
+ explored.add(t)
98
+ heapq.heappush(pq,t)
99
+ return - 1
100
+ ```
101
+ C++ 代码:
102
+ ``` C++
103
+ class Solution {
104
+ public:
105
+ int nthUglyNumber(int n) {
106
+ int nums[ ] = {2, 3, 5};
107
+ set<long > s;
108
+ priority_queue<long, vector<long >, greater<long >> q;
109
+ s.insert(1);
110
+ q.push(1);
111
+ for (int i = 1; i <= n; i++)
112
+ {
113
+ long x = q.top();
114
+ q.pop();
115
+ if (i == n)
116
+ return (int)x;
117
+ for (int num = 0; num < 3; num++)
118
+ {
119
+ long t = nums[ num] * x;
120
+ if (!s.count(t))
121
+ {
122
+ s.insert(t);
123
+ q.push(t);
124
+ }
125
+ }
126
+ }
127
+ return -1;
128
+ }
129
+ };
130
+ ```
79
131
* 时间复杂度:从优先队列中取最小值为 $O(1)$,往优先队列中添加元素复杂度为 $O(\log{n})$。整体复杂度为 $O(n\log{n})$
80
132
* 空间复杂度:$O(n)$
81
133
82
134
---
83
135
84
- ### 多路归并(多指针)解法
136
+ ### 多路归并(多指针)
85
137
86
138
从解法一中不难发现,我们「往后产生的丑数」都是基于「已有丑数」而来(使用「已有丑数」乘上「质因数」$2$、$3$、$5$)。
87
139
88
140
因此,如果我们所有丑数的有序序列为 $a1,a2,a3,...,an$ 的话,序列中的每一个数都必然能够被以下三个序列(中的至少一个)覆盖:
89
141
90
- * 由丑数 * $ 2$ 所得的有序序列:$1 * 2$、$2 * 2$、$3 * 2$、$4 * 2$、$5 * 2$、$6 * 2$、$8 * 2$ ...
91
- * 由丑数 * $ 3$ 所得的有序序列:$1 * 3$、$2 * 3$、$3 * 3$、$4 * 3$、$5 * 3$、$6 * 3$、$8 * 3$ ...
92
- * 由丑数 * $ 5$ 所得的有序序列:$1 * 5$、$2 * 5$、$3 * 5$、$4 * 5$、$5 * 5$、$6 * 5$、$8 * 5$ ...
142
+ * 由丑数 $\times 2$ 所得的有序序列:$1 \times 2$、$2 \times 2$、$3 \times 2$、$4 \times 2$、$5 \times 2$、$6 \times 2$、$8 \times 2$ ...
143
+ * 由丑数 $ \times 3$ 所得的有序序列:$1 \times 3$、$2 \times 3$、$3 \times 3$、$4 \times 3$、$5 \times 3$、$6 \times 3$、$8 \times 3$ ...
144
+ * 由丑数 $\times 5$ 所得的有序序列:$1 \times 5$、$2 \times 5$、$3 \times 5$、$4 \times 5$、$5 \times 5$、$6 \times 5$、$8 \times 5$ ...
93
145
94
146
举个🌰,假设我们需要求得 $[1, 2, 3, ... , 10, 12]$ 丑数序列 $arr$ 的最后一位,那么该序列可以看作以下三个有序序列归并而来:
95
147
96
- * $1 * 2, 2 * 2, 3 * 2, ... , 10 * 2, 12 * 2$ ,将 $2$ 提出,即 $arr * 2$
97
- * $1 * 3, 2 * 3, 3 * 3, ... , 10 * 3, 12 * 3$ ,将 $3$ 提出,即 $arr * 3$
98
- * $1 * 5, 2 * 5, 3 * 5, ... , 10 * 5, 12 * 5$ ,将 $5$ 提出,即 $arr * 5$
148
+ * $1 \times 2, 2 \times 2, 3 \times 2, ... , 10 \times 2, 12 \times 2$ ,将 $2$ 提出,即 $arr \times 2$
149
+ * $1 \times 3, 2 \times 3, 3 \times 3, ... , 10 \times 3, 12 \times 3$ ,将 $3$ 提出,即 $arr \times 3$
150
+ * $1 \times 5, 2 \times 5, 3 \times 5, ... , 10 \times 5, 12 \times 5$ ,将 $5$ 提出,即 $arr \times 5$
99
151
100
- 因此我们可以使用三个指针来指向目标序列 $arr$ 的某个下标(下标 $0$ 作为哨兵不使用,起始都为 $1$),使用 $arr[ 下标] * 质因数$ 代表当前使用到三个有序序列中的哪一位,同时使用 $idx$ 表示当前生成到 $arr$ 哪一位丑数。
152
+ 因此我们可以使用三个指针来指向目标序列 $arr$ 的某个下标(下标 $0$ 作为哨兵不使用,起始都为 $1$),使用 $arr[下标] \times 质因数$ 代表当前使用到三个有序序列中的哪一位,同时使用 $idx$ 表示当前生成到 $arr$ 哪一位丑数。
101
153
102
- 代码:
103
- ``` java
154
+ Java 代码:
155
+ ```Java
104
156
class Solution {
105
157
public int nthUglyNumber(int n) {
106
158
// ans 用作存储已有丑数(从下标 1 开始存储,第一个丑数为 1)
@@ -123,6 +175,80 @@ class Solution {
123
175
}
124
176
}
125
177
```
178
+ Python 代码:
179
+ ``` Python
180
+ class Solution (object ):
181
+ def nthUglyNumber (self , n ):
182
+ """
183
+ :type n: int
184
+ :rtype: int
185
+ """
186
+ # ans 用作存储已有丑数(从下标 1 开始存储,第一个丑数为 1)
187
+ ans = [0 ] * (n+ 1 )
188
+ ans[1 ] = 1
189
+ # 由于三个有序序列都是由「已有丑数」*「质因数」而来
190
+ # i2、i3 和 i5 分别代表三个有序序列当前使用到哪一位「已有丑数」下标(起始都指向 1)
191
+ i2 = i3 = i5 = 1
192
+ idx = 2
193
+ while idx <= n:
194
+ # 由 ans[iX] * X 可得当前有序序列指向哪一位
195
+ a,b,c = ans[i2] * 2 ,ans[i3]* 3 ,ans[i5]* 5
196
+ # 将三个有序序列中的最小一位存入「已有丑数」序列,并将其下标后移
197
+ m = min (a,b,c)
198
+ # 由于可能不同有序序列之间产生相同丑数,因此只要一样的丑数就跳过(不能使用 else if )
199
+ if m == a:
200
+ i2 += 1
201
+ if m == b:
202
+ i3 += 1
203
+ if m == c:
204
+ i5 += 1
205
+ ans[idx] = m
206
+ idx += 1
207
+ return ans[n]
208
+ ```
209
+ JavaScript 代码:
210
+ ``` JavaScript
211
+ /**
212
+ * @param {number} n
213
+ * @return {number}
214
+ */
215
+ var nthUglyNumber = function (n ) {
216
+ const ans = new Array (n+ 1 );
217
+ ans[1 ]= 1 ;
218
+ for (let i2= 1 , i3= 1 , i5= 1 , idx= 2 ;idx<= n;idx++ ){
219
+ let a= ans[i2]* 2 , b= ans[i3]* 3 , c= ans[i5]* 5 ;
220
+ let min= Math .min (a, b, c);
221
+ if (min=== a) i2++ ;
222
+ if (min=== b) i3++ ;
223
+ if (min=== c) i5++ ;
224
+ ans[idx]= min;
225
+ }
226
+ return ans[n];
227
+ };
228
+ ```
229
+ C++ 代码:
230
+ ``` C++
231
+ class Solution {
232
+ public:
233
+ int nthUglyNumber(int n) {
234
+ // 存储丑数
235
+ int * arr = new int[ n + 1] ;
236
+ arr[ 1] = 1;
237
+
238
+ for (int x = 1, y = 1, z = 1, index = 2; index <= n; index++){
239
+ int a = arr[ x] * 2, b = arr[ y] * 3, c = arr[ z] * 5;
240
+ int m = min(a, min(b, c));
241
+ if (m == a)x++;
242
+ if (m == b)y++;
243
+ if (m == c)z++;
244
+ arr[ index] = m;
245
+ }
246
+ int ans = arr[ n] ;
247
+ delete[ ] arr;
248
+ return ans;
249
+ }
250
+ };
251
+ ```
126
252
* 时间复杂度:$O(n)$
127
253
* 空间复杂度:$O(n)$
128
254
0 commit comments