@@ -44,7 +44,6 @@ \subsubsection{代码}
44
44
// 时间复杂度O(n),空间复杂度O(n)
45
45
class Solution {
46
46
public:
47
- typedef string state_t;
48
47
int ladderLength(string start, string end,
49
48
const unordered_set<string> &dict) {
50
49
if (start.size() != end.size()) return 0;
@@ -359,7 +358,44 @@ \subsection{代码模板}
359
358
对于树,如果用STL,可以用\fn {unordered_map<state_t, state_t > father}表示一颗树,代码非常简洁。如果能够预估状态总数的上限(设为STATE_MAX),可以用数组(\fn {state_t nodes[STATE_MAX]}),即树的双亲表示法来表示树,效率更高,当然,需要写更多代码。
360
359
361
360
361
+ \subsubsection {双队列的写法 }
362
362
\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
+
363
399
/**
364
400
* @brief 反向生成路径.
365
401
* @param[in] father 树
@@ -386,29 +422,36 @@ \subsection{代码模板}
386
422
* @brief 广搜.
387
423
* @param[in] state_t 状态,如整数,字符串,一维数组等
388
424
* @param[in] start 起点
389
- * @param[in] state_is_target 判断状态是否是目标的函数
390
- * @param[in] state_extend 状态扩展函数
425
+ * @param[in] grid 输入数据
391
426
* @return 从起点到目标状态的一条最短路径
392
427
*/
393
428
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) {
397
430
queue<state_t> next, current; // 当前层,下一层
398
431
unordered_set<state_t> visited; // 判重
399
- unordered_map<state_t, state_t> father;
432
+ unordered_map<state_t, state_t> father; // 树
400
433
401
434
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
+ };
404
446
405
447
current.push(start);
448
+ visited.insert(start);
406
449
while (!current.empty() && !found) {
407
450
++level;
408
451
while (!current.empty() && !found) {
409
452
const state_t state = current.front();
410
453
current.pop();
411
- vector<state_t> new_states = state_extend(state, visited );
454
+ vector<state_t> new_states = state_extend(state);
412
455
for (auto iter = new_states.cbegin();
413
456
iter != new_states.cend() && ! found; ++iter) {
414
457
const state_t new_state(*iter);
@@ -437,3 +480,71 @@ \subsection{代码模板}
437
480
}
438
481
}
439
482
\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