Skip to content

Commit 4a5e30e

Browse files
committed
做完了系列题
1 parent 6d9abfa commit 4a5e30e

12 files changed

+1406
-59
lines changed

C++/LeetCodet题解(C++版).pdf

140 KB
Binary file not shown.

C++/chapBruteforce.tex

Lines changed: 234 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,15 @@ \subsubsection{代码}
7777
public:
7878
vector<vector<int> > subsets(vector<int> &S) {
7979
vector<vector<int> > result;
80-
bool *selected = new bool[S.size()];
80+
vector<bool> selected(S.size(), false);
8181
std::sort(S.begin(), S.end()); // 本题对顺序有要求,需要排序
8282

8383
subsets(S, selected, 0, result);
84-
delete[] selected;
8584
return result;
8685
}
8786

8887
private:
89-
static void subsets(const vector<int> &S, bool selected[], int step,
88+
static void subsets(const vector<int> &S, vector<bool> &selected, int step,
9089
vector<vector<int> > &result) {
9190
if (step == S.size()) {
9291
vector<int> subset;
@@ -112,11 +111,12 @@ \subsection{二进制法}
112111

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

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

116116
\subsubsection{代码}
117117
\begin{Code}
118118
// LeetCode, Subsets
119-
// 二进制法
119+
// 二进制法,也可以看做是位向量法,只不过更加优化
120120
class Solution {
121121
public:
122122
vector<vector<int> > subsets(vector<int> &S) {
@@ -221,3 +221,233 @@ \subsubsection{相关题目}
221221
\begindot
222222
\item Subsets,见 \S \ref{sec:subsets}
223223
\myenddot
224+
225+
226+
\section{Permutations} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
227+
\label{sec:permutations}
228+
229+
230+
\subsection{next_permutation()}
231+
\label{sec:next-permutation}
232+
偷懒的做法,可以直接使用\fn{std::next_permutation}。如果是在OJ网站上,可以用这个API偷个懒;如果是在面试中,面试官肯定会让你重新实现。
233+
234+
\subsubsection{代码}
235+
\begin{Code}
236+
// LeetCode, Permutations
237+
class Solution {
238+
public:
239+
vector<vector<int> > permute(vector<int> &num) {
240+
vector<vector<int> > result;
241+
std::sort(num.begin(), num.end());
242+
243+
do {
244+
result.push_back(num);
245+
} while(std::next_permutation(num.begin(), num.end()));
246+
return result;
247+
}
248+
};
249+
\end{Code}
250+
251+
252+
\subsection{重新实现next_permutation()}
253+
\label{sec:next-permutation-implement}
254+
算法过程如图~\ref{fig:permutation}所示(来自\myurl{http://fisherlei.blogspot.com/2012/12/leetcode-next-permutation.html})。
255+
256+
\begin{center}
257+
\includegraphics[width=360pt]{next-permutation.png}\\
258+
\figcaption{下一个排列算法流程}\label{fig:permutation}
259+
\end{center}
260+
261+
262+
\subsubsection{代码}
263+
\begin{Code}
264+
// LeetCode, Permutations
265+
// 重新实现 next_permutation()
266+
class Solution {
267+
public:
268+
vector<vector<int>> permute(vector<int>& num) {
269+
sort(num.begin(), num.end());
270+
271+
vector<vector<int>> permutations;
272+
273+
do {
274+
permutations.push_back(num);
275+
} while (next_permutation(num.begin(), num.end()));
276+
277+
return permutations;
278+
}
279+
280+
template<typename BidiIt>
281+
bool next_permutation(BidiIt first, BidiIt last) {
282+
// Get a reversed range to simplify reversed traversal.
283+
const auto rfirst = reverse_iterator<BidiIt>(last);
284+
const auto rlast = reverse_iterator<BidiIt>(first);
285+
286+
// Begin from the second last element to the first element.
287+
auto pivot = next(rfirst);
288+
289+
// Find `pivot`, which is the first element that is no less than its
290+
// successor. `Prev` is used since `pivort` is a `reversed_iterator`.
291+
while (pivot != rlast and !(*pivot < *prev(pivot)))
292+
++pivot;
293+
294+
// No such elemenet found, current sequence is already the largest
295+
// permutation, then rearrange to the first permutation and return false.
296+
if (pivot == rlast) {
297+
reverse(rfirst, rlast);
298+
return false;
299+
}
300+
301+
// Scan from right to left, find the first element that is greater than
302+
// `pivot`.
303+
auto pos = find_if(rfirst, pivot, bind1st(less<int>(), *pivot));
304+
305+
swap(*pos, *pivot);
306+
reverse(rfirst, pivot);
307+
308+
return true;
309+
}
310+
};
311+
\end{Code}
312+
313+
314+
\subsection{深搜}
315+
本题是求路径本身,求所有解,函数参数需要标记当前走到了哪步,还需要中间结果的引用,最终结果的引用。
316+
317+
扩展节点,每次从左到右,选一个没有出现过的元素。
318+
319+
本题不需要判重,因为状态装换图是一颗有层次的树。收敛条件是当前走到了最后一个元素。
320+
321+
\subsubsection{代码}
322+
\begin{Code}
323+
// LeetCode, Permutations
324+
// 深搜
325+
class Solution {
326+
public:
327+
vector<vector<int> > permute(vector<int>& num) {
328+
std::sort(num.begin(), num.end());
329+
330+
vector<vector<int>> result;
331+
vector<int> p(num.size(), 0); // 中间结果
332+
333+
permute(num.begin(), num.end(), 0, p, result);
334+
return result;
335+
}
336+
private:
337+
typedef vector<int>::const_iterator Iter;
338+
void permute(Iter first, Iter last, int cur, vector<int> &p,
339+
vector<vector<int> > &result) {
340+
if ((first + cur) == last) { // 收敛条件
341+
result.push_back(p);
342+
}
343+
344+
// 扩展状态
345+
for (auto i = first; i != last; i++) {
346+
bool used = false;
347+
// 查找 *i 是否在p[0, cur) 中出现过
348+
for (auto j = p.begin(); j != p.begin() + cur; j++) {
349+
if (*i == *j) {
350+
used = true;
351+
break;
352+
}
353+
}
354+
if (!used) {
355+
p[cur] = *i;
356+
permute(first, last, cur + 1, p, result);
357+
// 不需要恢复P[cur],返回上层cur会自动减1,P[cur]会被覆盖
358+
}
359+
}
360+
}
361+
};
362+
\end{Code}
363+
364+
365+
\subsubsection{相关题目}
366+
\begindot
367+
\item Permutations II ,见 \S \ref{sec:permutations-ii}
368+
\myenddot
369+
370+
371+
\section{Permutations II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
372+
\label{sec:permutations-ii}
373+
374+
375+
\subsection{next_permutation()}
376+
上一题中的代码,见\S \ref{sec:next-permutation} 节,也适用于本题。
377+
378+
379+
\subsection{重新实现next_permutation()}
380+
上一题中的代码,见\S \ref{sec:next-permutation-implement} 节,也适用于本题。
381+
382+
383+
\subsection{深搜}
384+
递归函数\fn{permute()}的参数\fn{p},是中间结果,它的长度又能标记当前走到了哪一步,用于判断收敛条件。
385+
386+
扩展节点,每次从小到大,选一个没有被用光的元素,直到所有元素被用光。
387+
388+
本题不需要判重,因为状态装换图是一颗有层次的树。
389+
390+
391+
\subsubsection{代码}
392+
\begin{Code}
393+
// LeetCode, Permutations II
394+
// 深搜
395+
class Solution {
396+
public:
397+
vector<vector<int> > permuteUnique(vector<int>& num) {
398+
std::sort(num.begin(), num.end());
399+
400+
unordered_map<int, int> count_map; // 记录每个元素的出现次数
401+
std::for_each(num.begin(), num.end(), [&count_map](int e) {
402+
if (count_map.find(e) != count_map.end())
403+
count_map[e]++;
404+
else
405+
count_map[e] = 1;
406+
});
407+
408+
// 将map里的pair拷贝到一个vector里
409+
std::vector<pair<int, int> > elems;
410+
std::for_each(count_map.begin(), count_map.end(),
411+
[&elems](const pair<int, int> &e) {
412+
elems.push_back(e);
413+
});
414+
415+
vector<vector<int>> result; // 最终结果
416+
vector<int> p; // 中间结果
417+
418+
permute(num.size(), elems.begin(), elems.end(), 0, p, result);
419+
return result;
420+
}
421+
422+
private:
423+
typedef std::vector<pair<int, int> >::const_iterator Iter;
424+
425+
void permute(const size_t n, Iter first, Iter last,
426+
vector<int> &p, vector<vector<int> > &result) {
427+
if (n == p.size()) { // 收敛条件
428+
result.push_back(p);
429+
}
430+
431+
// 扩展状态
432+
for (auto i = first; i != last; i++) {
433+
int count = 0; // 统计 *i 在p中出现过多少次
434+
for (auto j = p.begin(); j != p.end(); j++) {
435+
if (i->first == *j) {
436+
count ++;
437+
}
438+
}
439+
if (count < i->second) {
440+
p.push_back(i->first);
441+
permute(n, first, last, p, result);
442+
p.pop_back(); // 撤销动作,返回上一层
443+
}
444+
}
445+
}
446+
};
447+
\end{Code}
448+
449+
450+
\subsubsection{相关题目}
451+
\begindot
452+
\item Permutations ,见 \S \ref{sec:permutations}
453+
\myenddot

0 commit comments

Comments
 (0)