Skip to content

Commit bbf9fc1

Browse files
committed
完成了140道题目
1 parent ab1fab9 commit bbf9fc1

23 files changed

+4586
-239
lines changed

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

291 KB
Binary file not shown.

C++/chapBFS.tex

Lines changed: 227 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ \chapter{广度优先搜索}
88

99

1010
\section{Word Ladder} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11-
\label{sec:wordladder}
11+
\label{sec:word-ladder}
1212

1313

1414
\subsubsection{描述}
@@ -95,12 +95,12 @@ \subsubsection{代码}
9595
\subsubsection{相关题目}
9696

9797
\begindot
98-
\item Word Ladder II,见 \S \ref{sec:wordladderii}
98+
\item Word Ladder II,见 \S \ref{sec:word-ladder-ii}
9999
\myenddot
100100

101101

102102
\section{Word Ladder II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103-
\label{sec:wordladderii}
103+
\label{sec:word-ladder-ii}
104104

105105

106106
\subsubsection{描述}
@@ -212,5 +212,228 @@ \subsubsection{代码}
212212
\subsubsection{相关题目}
213213

214214
\begindot
215-
\item Word Ladder,见 \S \ref{sec:wordladder}
215+
\item Word Ladder,见 \S \ref{sec:word-ladder}
216216
\myenddot
217+
218+
219+
\section{Surrounded Regions} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220+
\label{sec:surrounded-regions}
221+
222+
223+
\subsubsection{描述}
224+
Given a 2D board containing \fn{'X'} and \fn{'O'}, capture all regions surrounded by \fn{'X'}.
225+
226+
A region is captured by flipping all \fn{'O'}s into \fn{'X'}s in that surrounded region .
227+
228+
For example,
229+
\begin{Code}
230+
X X X X
231+
X O O X
232+
X X O X
233+
X O X X
234+
\end{Code}
235+
236+
After running your function, the board should be:
237+
\begin{Code}
238+
X X X X
239+
X X X X
240+
X X X X
241+
X O X X
242+
\end{Code}
243+
244+
245+
\subsubsection{分析}
246+
广搜。从上下左右四个边界往里走,凡是能碰到的\fn{'O'},都是跟边界接壤的,应该删除。
247+
248+
249+
\subsubsection{代码}
250+
\begin{Code}
251+
// LeetCode, Surrounded Regions
252+
// BFS
253+
class Solution {
254+
public:
255+
void solve(vector<vector<char>> &board) {
256+
if (board.size() == 0) return;
257+
258+
const int m = board.size();
259+
const int n = board[0].size();
260+
for (int i = 0; i < n; i++) {
261+
bfs(board, 0, i);
262+
bfs(board, m - 1, i);
263+
}
264+
for (int j = 1; j < m - 1; j++) {
265+
bfs(board, j, 0);
266+
bfs(board, j, n - 1);
267+
}
268+
for (int i = 0; i < n; i++)
269+
for (int j = 0; j < m; j++)
270+
if (board[i][j] == 'O')
271+
board[i][j] = 'X';
272+
else if (board[i][j] == '+')
273+
board[i][j] = 'O';
274+
}
275+
private:
276+
void bfs(vector<vector<char>> &board, int i, int j) {
277+
queue<int> q;
278+
visit(board, i, j, q);
279+
while (!q.empty()) {
280+
int cur = q.front(); q.pop();
281+
const int x = cur / board[0].size();
282+
const int y = cur % board[0].size();
283+
visit(board, x - 1, y, q);
284+
visit(board, x, y - 1, q);
285+
visit(board, x + 1, y, q);
286+
visit(board, x, y + 1, q);
287+
}
288+
}
289+
void visit(vector<vector<char>> &board, int i, int j, queue<int> &q) {
290+
const int m = board.size();
291+
const int n = board[0].size();
292+
if (i < 0 || i >= m || j < 0 || j >= n || board[i][j] != 'O')
293+
return;
294+
board[i][j] = '+'; // 既有标记功能又有去重功能
295+
q.push(i * n + j);
296+
}
297+
};
298+
\end{Code}
299+
300+
301+
\subsubsection{相关题目}
302+
303+
\begindot
304+
\item
305+
\myenddot
306+
307+
308+
\section{小结} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
309+
\label{sec:bfs-template}
310+
311+
312+
\subsection{适用场景}
313+
注意,这里的总结是一种经验,一种概率,不是绝对的结论!
314+
315+
\textbf{输入数据}:没什么特征,不像深搜,需要有“递归”的性质。如果是树或者图,概率更大。
316+
317+
\textbf{状态转换图}:树或者图。
318+
319+
\textbf{求解目标}:求最短。
320+
321+
322+
\subsection{思考的步骤}
323+
\begin{enumerate}
324+
\item 是求路径长度,还是路径本身(或动作序列)?
325+
\begin{enumerate}
326+
\item 如果是求路径长度,则状态里面要存路径长度
327+
\item 如果是求路径本身或动作序列
328+
\begin{enumerate}
329+
\item 要用一棵树存储宽搜过程中的路径
330+
\item 是否可以预估状态个数的上限?能够预估状态总数,则开一个大数组,用树的双亲表示法;如果不能预估状态总数,则要使用一棵通用的树。这一步也是第4步的必要不充分条件。
331+
\end{enumerate}
332+
\end{enumerate}
333+
334+
\item 如何表示状态?即一个状态需要存储哪些些必要的数据,才能够完整提供如何扩展到下一步状态的所有信息。一般记录当前位置或整体局面。
335+
336+
\item 如何扩展状态?这一步跟第2步相关。状态里记录的数据不同,扩展方法就不同。对于固定不变的数据结构(一般题目直接给出,作为输入数据),如二叉树,图等,扩展方法很简单,直接往下一层走,对于隐式图,要先在第1步里想清楚状态所带的数据,想清楚了这点,那如何扩展就很简单了。
337+
338+
\item 关于判重,状态是否存在完美哈希方案?即将状态一一映射到整数,互相之间不会冲突。
339+
\begin{enumerate}
340+
\item 如果不存在,则需要使用通用的哈希表(自己实现或用标准库,例如\fn{unordered_set})来判重;自己实现哈希表的话,如果能够预估状态个数的上限,则可以开两个数组,head和next,表示哈希表,参考第 \S \ref{subsec:eightDigits}节方案2。
341+
\item 如果存在,则可以开一个大布尔数组,作为哈希表来判重,且此时可以精确计算出状态总数,而不仅仅是预估上限。
342+
\end{enumerate}
343+
344+
\item 目标状态是否已知?如果题目已经给出了目标状态,可以带来很大便利,这时候可以从起始状态出发,正向广搜;也可以从目标状态出发,逆向广搜;也可以同时出发,双向广搜。
345+
\end{enumerate}
346+
347+
348+
\subsection{代码模板}
349+
广搜需要一个队列,用于一层一层扩展,一个hashset,用于判重,一棵树(只求长度时不需要),用于存储整棵树。
350+
351+
对于队列,可以用\fn{queue},也可以把\fn{vector}当做队列使用。当求长度时,有两种做法:
352+
\begin{enumerate}
353+
\item 只用一个队列,但在状态结构体\fn{state_t}里增加一个整数字段\fn{step},表示走到当前状态用了多少步,当碰到目标状态,直接输出\fn{step}即可。这个方案,可以很方便的变成A*算法,把队列换成优先队列即可。
354+
\item 用两个队列,\fn{current, next},分别表示当前层次和下一层,另设一个全局整数\fn{level},表示层数(也即路径长度),当碰到目标状态,输出\fn{level}即可。这个方案,状态可以少一个字段,节省内存。
355+
\end{enumerate}
356+
357+
对于hashset,如果有完美哈希方案,用布尔数组(\fn{bool visited[STATE_MAX]}或\fn{vector<bool> visited(STATE_MAX, false)})来表示;如果没有,可以用STL里的\fn{set}或\fn{unordered_set}。
358+
359+
对于树,如果用STL,可以用\fn{unordered_map<state_t, state_t > father}表示一颗树,代码非常简洁。如果能够预估状态总数的上限(设为STATE_MAX),可以用数组(\fn{state_t nodes[STATE_MAX]}),即树的双亲表示法来表示树,效率更高,当然,需要写更多代码。
360+
361+
362+
\begin{Codex}[label=bfs_template1.cpp]
363+
/**
364+
* @brief 反向生成路径.
365+
* @param[in] father 树
366+
* @param[in] target 目标节点
367+
* @return 从起点到target的路径
368+
*/
369+
template<typename state_t>
370+
vector<state_t> gen_path(const unordered_map<state_t, state_t> &father,
371+
const state_t &target) {
372+
vector<state_t> path;
373+
path.push_back(target);
374+
375+
state_t cur = target;
376+
while (father.find(cur) != father.end()) {
377+
cur = father.at(cur);
378+
path.push_back(cur);
379+
}
380+
reverse(path.begin(), path.end());
381+
382+
return path;
383+
}
384+
385+
/**
386+
* @brief 广搜.
387+
* @param[in] state_t 状态,如整数,字符串,一维数组等
388+
* @param[in] start 起点
389+
* @param[in] state_is_target 判断状态是否是目标的函数
390+
* @param[in] state_extend 状态扩展函数
391+
* @return 从起点到目标状态的一条最短路径
392+
*/
393+
template<typename state_t>
394+
vector<state_t> bfs(state_t &start, bool (*state_is_target)(const state_t&),
395+
vector<state_t>(*state_extend)(const state_t&,
396+
unordered_set<string> &visited)) {
397+
queue<state_t> next, current; // 当前层,下一层
398+
unordered_set<state_t> visited; // 判重
399+
unordered_map<state_t, state_t> father;
400+
401+
int level = 0; // 层次
402+
bool found = false;
403+
state_t target;
404+
405+
current.push(start);
406+
while (!current.empty() && !found) {
407+
++level;
408+
while (!current.empty() && !found) {
409+
const state_t state = current.front();
410+
current.pop();
411+
vector<state_t> new_states = state_extend(state, visited);
412+
for (auto iter = new_states.begin();
413+
iter != new_states.end() && ! found; ++iter) {
414+
const state_t new_state(*iter);
415+
416+
if (state_is_target(new_state)) {
417+
found = true; //找到了
418+
target = new_state;
419+
father[new_state] = state;
420+
break;
421+
}
422+
423+
next.push(new_state);
424+
// visited.insert(new_state); 必须放到 state_extend()里
425+
father[new_state] = state;
426+
}
427+
}
428+
swap(next, current); //!!! 交换两个队列
429+
}
430+
431+
if (found) {
432+
return gen_path(father, target);
433+
//return level + 1;
434+
} else {
435+
return vector<state_t>();
436+
//return 0;
437+
}
438+
}
439+
\end{Codex}

0 commit comments

Comments
 (0)