Skip to content

Commit a975bc1

Browse files
committed
重构
1 parent d3b0589 commit a975bc1

File tree

1 file changed

+157
-35
lines changed

1 file changed

+157
-35
lines changed

C++/chapBruteforce.tex

Lines changed: 157 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,57 +29,56 @@ \subsubsection{描述}
2929
\end{Code}
3030

3131

32-
\subsection{增量构造法}
32+
\subsection{递归}
33+
34+
35+
\subsubsection{增量构造法}
3336
每个元素,都有两种选择,选或者不选。
3437

35-
\subsubsection{代码}
3638
\begin{Code}
3739
// LeetCode, Subsets
38-
// 增量构造法,朴素深搜
40+
// 增量构造法,深搜,时间复杂度O(2^n),空间复杂度O(n)
3941
class Solution {
4042
public:
4143
vector<vector<int> > subsets(vector<int> &S) {
44+
sort(S.begin(), S.end()); // 输出要求有序
4245
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);
4748
return result;
4849
}
4950

5051
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,
5253
vector<vector<int> > &result) {
5354
if (step == S.size()) {
54-
result.push_back(cur);
55+
result.push_back(path);
5556
return;
5657
}
5758
// 不选S[step]
58-
subsets(S, cur, step + 1, result);
59+
subsets(S, path, step + 1, result);
5960
// 选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();
6364
}
6465
};
6566
\end{Code}
6667

6768

68-
\subsection{位向量法}
69+
\subsubsection{位向量法}
6970
开一个位向量\fn{bool selected[n]},每个元素可以选或者不选。
7071

71-
72-
\subsubsection{代码}
7372
\begin{Code}
7473
// LeetCode, Subsets
75-
// 位向量法,也属于朴素深搜
74+
// 位向量法,深搜,时间复杂度O(2^n),空间复杂度O(n)
7675
class Solution {
7776
public:
7877
vector<vector<int> > subsets(vector<int> &S) {
78+
sort(S.begin(), S.end()); // 输出要求有序
79+
7980
vector<vector<int> > result;
8081
vector<bool> selected(S.size(), false);
81-
sort(S.begin(), S.end()); // 本题对顺序有要求,需要排序
82-
8382
subsets(S, selected, 0, result);
8483
return result;
8584
}
@@ -106,22 +105,47 @@ \subsubsection{代码}
106105
\end{Code}
107106

108107

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{二进制法}
110135
本方法的前提是:集合的元素不超过int位数。用一个int整数表示位向量,第$i$位为1,则表示选择$S[i]$,为0则不选择。例如\fn{S=\{A,B,C,D\}},则\fn{0110=6}表示子集\fn{\{B,C\}}。
111136

112137
这种方法最巧妙。因为它不仅能生成子集,还能方便的表示集合的并、交、差等集合运算。设两个集合的位向量分别为$B_1$$B_2$,则$B_1|B_2, B_1 \& B_2, B_1 \^ B_2$分别对应集合的并、交、对称差。
113138

114139
二进制法,也可以看做是位向量法,只不过更加优化。
115140

116-
\subsubsection{代码}
117141
\begin{Code}
118142
// LeetCode, Subsets
119-
// 二进制法
143+
// 二进制法,时间复杂度O(2^n),空间复杂度O(1)
120144
class Solution {
121145
public:
122146
vector<vector<int> > subsets(vector<int> &S) {
147+
sort(S.begin(), S.end()); // 输出要求有序
123148
vector<vector<int> > result;
124-
sort(S.begin(), S.end()); // 本题对顺序有要求,需要排序
125149
const size_t n = S.size();
126150
vector<int> v;
127151

@@ -173,15 +197,48 @@ \subsubsection{分析}
173197
这题有重复元素,但本质上,跟上一题很类似,上一题中元素没有重复,相当于每个元素只能选0或1次,这里扩充到了每个元素可以选0到若干次而已。
174198

175199

176-
\subsubsection{代码}
200+
\subsection{递归}
201+
202+
203+
\subsubsection{增量构造法}
177204
\begin{Code}
178205
// LeetCode, Subsets II
179-
// 增量构造法
206+
// 增量构造法,版本1,时间复杂度O(2^n),空间复杂度O(n)
180207
class Solution {
181208
public:
182209
vector<vector<int> > subsetsWithDup(vector<int> &S) {
210+
sort(S.begin(), S.end()); // 必须排序
211+
183212
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()); // 必须排序
185242

186243
unordered_map<int, int> count_map; // 记录每个元素的出现次数
187244
for_each(S.begin(), S.end(), [&count_map](int e) {
@@ -225,14 +282,16 @@ \subsubsection{代码}
225282
};
226283
\end{Code}
227284

285+
286+
\subsubsection{位向量法}
228287
\begin{Code}
229288
// LeetCode, Subsets II
230-
// 位向量法
289+
// 位向量法,时间复杂度O(2^n),空间复杂度O(n)
231290
class Solution {
232291
public:
233292
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());
236295
vector<int> count(S.back() - S.front() + 1, 0);
237296
// 计算所有元素的个数
238297
for (auto i : S) {
@@ -269,6 +328,66 @@ \subsubsection{代码}
269328
\end{Code}
270329

271330

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+
272391
\subsubsection{相关题目}
273392
\begindot
274393
\item Subsets,见 \S \ref{sec:subsets}
@@ -293,6 +412,7 @@ \subsection{next_permutation()}
293412
\subsubsection{代码}
294413
\begin{Code}
295414
// LeetCode, Permutations
415+
// 时间复杂度O(n!),空间复杂度O(1)
296416
class Solution {
297417
public:
298418
vector<vector<int> > permute(vector<int> &num) {
@@ -316,6 +436,7 @@ \subsubsection{代码}
316436
\begin{Code}
317437
// LeetCode, Permutations
318438
// 重新实现 next_permutation()
439+
// 时间复杂度O(n!),空间复杂度O(1)
319440
class Solution {
320441
public:
321442
vector<vector<int>> permute(vector<int>& num) {
@@ -344,6 +465,7 @@ \subsubsection{代码}
344465
\begin{Code}
345466
// LeetCode, Permutations
346467
// 深搜,增量构造法
468+
// 时间复杂度O(n!),空间复杂度O(n)
347469
class Solution {
348470
public:
349471
vector<vector<int> > permute(vector<int>& num) {
@@ -419,7 +541,7 @@ \subsection{深搜}
419541
\subsubsection{代码}
420542
\begin{Code}
421543
// LeetCode, Permutations II
422-
// 深搜
544+
// 深搜,时间复杂度O(n!),空间复杂度O(n)
423545
class Solution {
424546
public:
425547
vector<vector<int> > permuteUnique(vector<int>& num) {
@@ -507,14 +629,11 @@ \subsubsection{描述}
507629
\end{Code}
508630

509631

510-
\subsubsection{分析}
511-
生成组合问题。
512-
513-
514-
\subsubsection{代码}
632+
\subsection{深搜}
515633
\begin{Code}
516634
// LeetCode, Combinations
517635
// 深搜,递归
636+
// 时间复杂度O(n!),空间复杂度O(n)
518637
class Solution {
519638
public:
520639
vector<vector<int> > combine(int n, int k) {
@@ -539,9 +658,12 @@ \subsubsection{代码}
539658
};
540659
\end{Code}
541660

661+
662+
\subsection{迭代}
542663
\begin{Code}
543664
// LeetCode, Combinations
544665
// use prev_permutation()
666+
// 时间复杂度O(n!),空间复杂度O(n)
545667
class Solution {
546668
public:
547669
vector<vector<int> > combine(int n, int k) {

0 commit comments

Comments
 (0)