@@ -29,57 +29,56 @@ \subsubsection{描述}
29
29
\end {Code }
30
30
31
31
32
- \subsection {增量构造法 }
32
+ \subsection {递归 }
33
+
34
+
35
+ \subsubsection {增量构造法 }
33
36
每个元素,都有两种选择,选或者不选。
34
37
35
- \subsubsection {代码 }
36
38
\begin {Code }
37
39
// LeetCode, Subsets
38
- // 增量构造法,朴素深搜
40
+ // 增量构造法,深搜,时间复杂度O(2^n),空间复杂度O(n)
39
41
class Solution {
40
42
public:
41
43
vector<vector<int> > subsets(vector<int> &S) {
44
+ sort(S.begin(), S.end()); // 输出要求有序
42
45
vector<vector<int> > result;
43
- vector<int> cur;
44
- sort(S.begin(), S.end()); // 本题对顺序有要求,需要排序
45
-
46
- subsets(S, cur, 0, result);
46
+ vector<int> path;
47
+ subsets(S, path, 0, result);
47
48
return result;
48
49
}
49
50
50
51
private:
51
- static void subsets(const vector<int> &S, vector<int> &cur , int step,
52
+ static void subsets(const vector<int> &S, vector<int> &path , int step,
52
53
vector<vector<int> > &result) {
53
54
if (step == S.size()) {
54
- result.push_back(cur );
55
+ result.push_back(path );
55
56
return;
56
57
}
57
58
// 不选S[step]
58
- subsets(S, cur , step + 1, result);
59
+ subsets(S, path , step + 1, result);
59
60
// 选S[step]
60
- cur .push_back(S[step]);
61
- subsets(S, cur , step + 1, result);
62
- cur .pop_back();
61
+ path .push_back(S[step]);
62
+ subsets(S, path , step + 1, result);
63
+ path .pop_back();
63
64
}
64
65
};
65
66
\end {Code }
66
67
67
68
68
- \subsection {位向量法 }
69
+ \subsubsection {位向量法 }
69
70
开一个位向量\fn {bool selected[n]},每个元素可以选或者不选。
70
71
71
-
72
- \subsubsection {代码 }
73
72
\begin {Code }
74
73
// LeetCode, Subsets
75
- // 位向量法,也属于朴素深搜
74
+ // 位向量法,深搜,时间复杂度O(2^n),空间复杂度O(n)
76
75
class Solution {
77
76
public:
78
77
vector<vector<int> > subsets(vector<int> &S) {
78
+ sort(S.begin(), S.end()); // 输出要求有序
79
+
79
80
vector<vector<int> > result;
80
81
vector<bool> selected(S.size(), false);
81
- sort(S.begin(), S.end()); // 本题对顺序有要求,需要排序
82
-
83
82
subsets(S, selected, 0, result);
84
83
return result;
85
84
}
@@ -106,22 +105,47 @@ \subsubsection{代码}
106
105
\end {Code }
107
106
108
107
109
- \subsection {二进制法 }
108
+ \subsection {迭代 }
109
+
110
+
111
+ \subsubsection {增量构造法 }
112
+ \begin {Code }
113
+ // LeetCode, Subsets
114
+ // 迭代版,时间复杂度O(2^n),空间复杂度O(1)
115
+ class Solution {
116
+ public:
117
+ vector<vector<int> > subsets(vector<int> &S) {
118
+ sort(S.begin(), S.end()); // 输出要求有序
119
+ vector<vector<int> > result(1);
120
+ for (auto elem : S) {
121
+ result.reserve(result.size() * 2);
122
+ auto half = result.begin() + result.size();
123
+ copy(result.begin(), half, back_inserter(result));
124
+ for_each(half, result.end(), [&elem](decltype(result[0]) &e){
125
+ e.push_back(elem);
126
+ });
127
+ }
128
+ return result;
129
+ }
130
+ };
131
+ \end {Code }
132
+
133
+
134
+ \subsubsection {二进制法 }
110
135
本方法的前提是:集合的元素不超过int位数。用一个int整数表示位向量,第$ i$ 位为1,则表示选择$ S[i]$ ,为0则不选择。例如\fn {S=\{ A,B,C,D\} },则\fn {0110=6}表示子集\fn {\{ B,C\} }。
111
136
112
137
这种方法最巧妙。因为它不仅能生成子集,还能方便的表示集合的并、交、差等集合运算。设两个集合的位向量分别为$ B_1 $ 和$ B_2 $ ,则$ B_1 |B_2 , B_1 \& B_2 , B_1 \^ B_2 $ 分别对应集合的并、交、对称差。
113
138
114
139
二进制法,也可以看做是位向量法,只不过更加优化。
115
140
116
- \subsubsection {代码 }
117
141
\begin {Code }
118
142
// LeetCode, Subsets
119
- // 二进制法
143
+ // 二进制法,时间复杂度O(2^n),空间复杂度O(1)
120
144
class Solution {
121
145
public:
122
146
vector<vector<int> > subsets(vector<int> &S) {
147
+ sort(S.begin(), S.end()); // 输出要求有序
123
148
vector<vector<int> > result;
124
- sort(S.begin(), S.end()); // 本题对顺序有要求,需要排序
125
149
const size_t n = S.size();
126
150
vector<int> v;
127
151
@@ -173,15 +197,48 @@ \subsubsection{分析}
173
197
这题有重复元素,但本质上,跟上一题很类似,上一题中元素没有重复,相当于每个元素只能选0或1次,这里扩充到了每个元素可以选0到若干次而已。
174
198
175
199
176
- \subsubsection {代码 }
200
+ \subsection {递归 }
201
+
202
+
203
+ \subsubsection {增量构造法 }
177
204
\begin {Code }
178
205
// LeetCode, Subsets II
179
- // 增量构造法
206
+ // 增量构造法,版本1,时间复杂度O(2^n),空间复杂度O(n)
180
207
class Solution {
181
208
public:
182
209
vector<vector<int> > subsetsWithDup(vector<int> &S) {
210
+ sort(S.begin(), S.end()); // 必须排序
211
+
183
212
vector<vector<int> > result;
184
- sort(S.begin(), S.end()); // 本题对顺序有要求,需要排序
213
+ vector<int> path;
214
+
215
+ dfs(S, S.begin(), path, result);
216
+ return result;
217
+ }
218
+
219
+ private:
220
+ static void dfs(const vector<int> &S, vector<int>::iterator start,
221
+ vector<int> &path, vector<vector<int> > &result) {
222
+ result.push_back(path);
223
+
224
+ for (auto i = start; i < S.end(); i++) {
225
+ if (i != start && *i == *(i-1)) continue;
226
+ path.push_back(*i);
227
+ dfs(S, i + 1, path, result);
228
+ path.pop_back();
229
+ }
230
+ }
231
+ };
232
+ \end {Code }
233
+
234
+ \begin {Code }
235
+ // LeetCode, Subsets II
236
+ // 增量构造法,版本2,时间复杂度O(2^n),空间复杂度O(n)
237
+ class Solution {
238
+ public:
239
+ vector<vector<int> > subsetsWithDup(vector<int> &S) {
240
+ vector<vector<int> > result;
241
+ sort(S.begin(), S.end()); // 必须排序
185
242
186
243
unordered_map<int, int> count_map; // 记录每个元素的出现次数
187
244
for_each(S.begin(), S.end(), [&count_map](int e) {
@@ -225,14 +282,16 @@ \subsubsection{代码}
225
282
};
226
283
\end {Code }
227
284
285
+
286
+ \subsubsection {位向量法 }
228
287
\begin {Code }
229
288
// LeetCode, Subsets II
230
- // 位向量法
289
+ // 位向量法,时间复杂度O(2^n),空间复杂度O(n)
231
290
class Solution {
232
291
public:
233
292
vector<vector<int> > subsetsWithDup(vector<int> &S) {
234
- vector<vector<int> > result;
235
- sort(S.begin(), S.end()); // 本题对顺序有要求,需要排序
293
+ vector<vector<int> > result; // 必须排序
294
+ sort(S.begin(), S.end());
236
295
vector<int> count(S.back() - S.front() + 1, 0);
237
296
// 计算所有元素的个数
238
297
for (auto i : S) {
@@ -269,6 +328,66 @@ \subsubsection{代码}
269
328
\end {Code }
270
329
271
330
331
+ \subsection {迭代 }
332
+
333
+
334
+ \subsubsection {增量构造法 }
335
+ \begin {Code }
336
+ // LeetCode, Subsets II
337
+ // 增量构造法
338
+ // 时间复杂度O(2^n),空间复杂度O(1)
339
+ class Solution {
340
+ public:
341
+ vector<vector<int> > subsetsWithDup(vector<int> &S) {
342
+ sort(S.begin(), S.end()); // 必须排序
343
+ vector<vector<int> > result(1);
344
+
345
+ size_t previous_size = 0;
346
+ for (size_t i = 0; i < S.size(); ++i) {
347
+ const size_t size = result.size();
348
+ for (size_t j = 0; j < size; ++j) {
349
+ if (i == 0 || S[i] != S[i-1] || j >= previous_size) {
350
+ result.push_back(result[j]);
351
+ result.back().push_back(S[i]);
352
+ }
353
+ }
354
+ previous_size = size;
355
+ }
356
+ return result;
357
+ }
358
+ };
359
+ \end {Code }
360
+
361
+
362
+ \subsubsection {二进制法 }
363
+ \begin {Code }
364
+ // LeetCode, Subsets II
365
+ // 二进制法,时间复杂度O(2^n),空间复杂度O(1)
366
+ class Solution {
367
+ public:
368
+ vector<vector<int> > subsetsWithDup(vector<int> &S) {
369
+ sort(S.begin(), S.end()); // 必须排序
370
+ // 用 set 去重,不能用 unordered_set,因为输出要求有序
371
+ set<vector<int> > result;
372
+ const size_t n = S.size();
373
+ vector<int> v;
374
+
375
+ for (size_t i = 0; i < 1U << n; ++i) {
376
+ for (size_t j = 0; j < n; ++j) {
377
+ if (i & 1 << j)
378
+ v.push_back(S[j]);
379
+ }
380
+ result.insert(v);
381
+ v.clear();
382
+ }
383
+ vector<vector<int> > real_result;
384
+ copy(result.begin(), result.end(), back_inserter(real_result));
385
+ return real_result;
386
+ }
387
+ };
388
+ \end {Code }
389
+
390
+
272
391
\subsubsection {相关题目 }
273
392
\begindot
274
393
\item Subsets,见 \S \ref {sec:subsets }
@@ -293,6 +412,7 @@ \subsection{next_permutation()}
293
412
\subsubsection {代码 }
294
413
\begin {Code }
295
414
// LeetCode, Permutations
415
+ // 时间复杂度O(n!),空间复杂度O(1)
296
416
class Solution {
297
417
public:
298
418
vector<vector<int> > permute(vector<int> &num) {
@@ -316,6 +436,7 @@ \subsubsection{代码}
316
436
\begin {Code }
317
437
// LeetCode, Permutations
318
438
// 重新实现 next_permutation()
439
+ // 时间复杂度O(n!),空间复杂度O(1)
319
440
class Solution {
320
441
public:
321
442
vector<vector<int>> permute(vector<int>& num) {
@@ -344,6 +465,7 @@ \subsubsection{代码}
344
465
\begin {Code }
345
466
// LeetCode, Permutations
346
467
// 深搜,增量构造法
468
+ // 时间复杂度O(n!),空间复杂度O(n)
347
469
class Solution {
348
470
public:
349
471
vector<vector<int> > permute(vector<int>& num) {
@@ -419,7 +541,7 @@ \subsection{深搜}
419
541
\subsubsection {代码 }
420
542
\begin {Code }
421
543
// LeetCode, Permutations II
422
- // 深搜
544
+ // 深搜,时间复杂度O(n!),空间复杂度O(n)
423
545
class Solution {
424
546
public:
425
547
vector<vector<int> > permuteUnique(vector<int>& num) {
@@ -507,14 +629,11 @@ \subsubsection{描述}
507
629
\end {Code }
508
630
509
631
510
- \subsubsection {分析 }
511
- 生成组合问题。
512
-
513
-
514
- \subsubsection {代码 }
632
+ \subsection {深搜 }
515
633
\begin {Code }
516
634
// LeetCode, Combinations
517
635
// 深搜,递归
636
+ // 时间复杂度O(n!),空间复杂度O(n)
518
637
class Solution {
519
638
public:
520
639
vector<vector<int> > combine(int n, int k) {
@@ -539,9 +658,12 @@ \subsubsection{代码}
539
658
};
540
659
\end {Code }
541
660
661
+
662
+ \subsection {迭代 }
542
663
\begin {Code }
543
664
// LeetCode, Combinations
544
665
// use prev_permutation()
666
+ // 时间复杂度O(n!),空间复杂度O(n)
545
667
class Solution {
546
668
public:
547
669
vector<vector<int> > combine(int n, int k) {
0 commit comments