Skip to content

Commit ec80dbb

Browse files
committed
完善广搜模板
1 parent 91a1070 commit ec80dbb

File tree

2 files changed

+121
-10
lines changed

2 files changed

+121
-10
lines changed

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

9.91 KB
Binary file not shown.

C++/chapBFS.tex

Lines changed: 121 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ \subsubsection{代码}
4444
// 时间复杂度O(n),空间复杂度O(n)
4545
class Solution {
4646
public:
47-
typedef string state_t;
4847
int ladderLength(string start, string end,
4948
const unordered_set<string> &dict) {
5049
if (start.size() != end.size()) return 0;
@@ -359,7 +358,44 @@ \subsection{代码模板}
359358
对于树,如果用STL,可以用\fn{unordered_map<state_t, state_t > father}表示一颗树,代码非常简洁。如果能够预估状态总数的上限(设为STATE_MAX),可以用数组(\fn{state_t nodes[STATE_MAX]}),即树的双亲表示法来表示树,效率更高,当然,需要写更多代码。
360359

361360

361+
\subsubsection{双队列的写法}
362362
\begin{Codex}[label=bfs_template1.cpp]
363+
/** 状态 */
364+
struct state_t {
365+
int data1; /** 状态的数据,可以有多个字段. */
366+
int data2; /** 状态的数据,可以有多个字段. */
367+
// dataN; /** 其他字段 */
368+
int action; /** 由父状态移动到本状态的动作,求动作序列时需要. */
369+
int count; /** 所花费的步骤数(也即路径长度-1),求路径长度时需要;
370+
不过,采用双队列时不需要本字段,只需全局设一个整数 */
371+
bool operator==(const state_t &other) const {
372+
return true; // 根据具体问题实现
373+
}
374+
};
375+
376+
// 定义hash函数
377+
378+
// 方法1:模板特化,当hash函数只需要状态本身,不需要其他数据时,用这个方法比较简洁
379+
namespace std {
380+
template<> struct hash<state_t> {
381+
size_t operator()(const state_t & x) const {
382+
return 0; // 根据具体问题实现
383+
}
384+
};
385+
}
386+
387+
// 方法2:函数对象,如果hash函数需要运行时数据,则用这种方法
388+
class Hasher {
389+
public:
390+
Hasher(int _m) : m(_m) {};
391+
size_t operator()(const state_t &s) const {
392+
return 0; // 根据具体问题实现
393+
}
394+
private:
395+
int m; // 存放外面传入的数据
396+
};
397+
398+
363399
/**
364400
* @brief 反向生成路径.
365401
* @param[in] father 树
@@ -386,29 +422,36 @@ \subsection{代码模板}
386422
* @brief 广搜.
387423
* @param[in] state_t 状态,如整数,字符串,一维数组等
388424
* @param[in] start 起点
389-
* @param[in] state_is_target 判断状态是否是目标的函数
390-
* @param[in] state_extend 状态扩展函数
425+
* @param[in] grid 输入数据
391426
* @return 从起点到目标状态的一条最短路径
392427
*/
393428
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)) {
429+
vector<state_t> bfs(state_t &start, const vector<vector<int>> &grid) {
397430
queue<state_t> next, current; // 当前层,下一层
398431
unordered_set<state_t> visited; // 判重
399-
unordered_map<state_t, state_t> father;
432+
unordered_map<state_t, state_t> father; // 树
400433

401434
int level = 0; // 层次
402-
bool found = false;
403-
state_t target;
435+
bool found = false; // 是否找到目标
436+
state_t target; // 符合条件的目标状态
437+
438+
// 判断当前状态是否为所求目标
439+
auto state_is_target = [&](const state_t &s) {return true; };
440+
// 扩展当前状态
441+
auto state_extend = [&](const state_t &s) {
442+
vector<state_t> result;
443+
// ...
444+
return result;
445+
};
404446

405447
current.push(start);
448+
visited.insert(start);
406449
while (!current.empty() && !found) {
407450
++level;
408451
while (!current.empty() && !found) {
409452
const state_t state = current.front();
410453
current.pop();
411-
vector<state_t> new_states = state_extend(state, visited);
454+
vector<state_t> new_states = state_extend(state);
412455
for (auto iter = new_states.cbegin();
413456
iter != new_states.cend() && ! found; ++iter) {
414457
const state_t new_state(*iter);
@@ -437,3 +480,71 @@ \subsection{代码模板}
437480
}
438481
}
439482
\end{Codex}
483+
484+
485+
\subsubsection{只用一个队列的写法}
486+
双队列的写法,当求路径长度时,不需要在状态里设置一个\fn{count}字段记录路径长度,只需全局设置一个整数\fn{level},比较节省内存;只用一个队列的写法,当求路径长度时,需要在状态里设置一个\fn{count}字段,不过,这种写法有一个好处 —— 可以很容易的变为A*算法,把\fn{queue}替换为\fn{priority_queue}即可。
487+
488+
\begin{Codex}[label=bfs_template2.cpp]
489+
// 与模板1相同的部分,不再重复
490+
// ...
491+
492+
/**
493+
* @brief 广搜.
494+
* @param[in] state_t 状态,如整数,字符串,一维数组等
495+
* @param[in] start 起点
496+
* @param[in] grid 输入数据
497+
* @return 从起点到目标状态的一条最短路径
498+
*/
499+
template<typename state_t>
500+
vector<state_t> bfs(state_t &start, const vector<vector<int>> &grid) {
501+
queue<state_t> q; // 队列
502+
unordered_set<state_t> visited; // 判重
503+
unordered_map<state_t, state_t> father; // 树
504+
505+
int level = 0; // 层次
506+
bool found = false; // 是否找到目标
507+
state_t target; // 符合条件的目标状态
508+
509+
// 判断当前状态是否为所求目标
510+
auto state_is_target = [&](const state_t &s) {return true; };
511+
// 扩展当前状态
512+
auto state_extend = [&](const state_t &s) {
513+
vector<state_t> result;
514+
// ...
515+
return result;
516+
};
517+
518+
start.count = 0;
519+
q.push(start);
520+
visited.insert(start);
521+
while (!q.empty() && !found) {
522+
const state_t state = q.front();
523+
q.pop();
524+
vector<state_t> new_states = state_extend(state);
525+
for (auto iter = new_states.cbegin();
526+
iter != new_states.cend() && ! found; ++iter) {
527+
const state_t new_state(*iter);
528+
529+
if (state_is_target(new_state)) {
530+
found = true; //找到了
531+
target = new_state;
532+
father[new_state] = state;
533+
break;
534+
}
535+
536+
q.push(new_state);
537+
// visited.insert(new_state); 必须放到 state_extend()里
538+
father[new_state] = state;
539+
}
540+
}
541+
542+
if (found) {
543+
return gen_path(father, target);
544+
//return level + 1;
545+
} else {
546+
return vector<state_t>();
547+
//return 0;
548+
}
549+
}
550+
\end{Codex}

0 commit comments

Comments
 (0)