当前位置:   article > 正文

LeetCode-双路BFS_leetcode 双向bfs

leetcode 双向bfs

1 开密码锁

剑指 Offer II 109. 开密码锁

752. 打开转盘锁

  1. class Solution {
  2. public:
  3. int openLock(vector<string>& deadends, string target) {
  4. //记录需要跳过的死亡密码
  5. unordered_set<string> deads(deadends.begin(), deadends.end());
  6. //记录已经穷举过的密码,防止走回头路
  7. unordered_set<string> visited;
  8. //用集合而不用队列,可以快速判断元素是否存在
  9. unordered_set<string> s1, s2;
  10. //从起点开始启动广度优先搜索
  11. int step = 0;
  12. s1.insert("0000");
  13. s2.insert(target);
  14. while (!s1.empty() && !s2.empty())
  15. {
  16. //保证从节点较小的那个集合开始广度遍历,这里固定从s1开始
  17. if (s2.size() < s1.size())
  18. {
  19. s1.swap(s2);
  20. }
  21. //哈希集合在遍历过程中不能修改,故此处用temp来存储扩散结果
  22. unordered_set<string> temp;
  23. //将当前队列中的所有节点向周围扩散
  24. for (auto& cur : s1)
  25. {
  26. //判断是否到达终点
  27. if (deads.count(cur))
  28. {
  29. continue;
  30. }
  31. //如果本次遍历的邻居就是目标节点,则找到一条从起始到目标节点的最短路径
  32. if (s2.count(cur))
  33. {
  34. return step;
  35. }
  36. visited.insert(cur);
  37. //将一个节点的未遍历的相邻节点加入到队列中
  38. for (int j = 0; j < 4; ++j)
  39. {
  40. string up = plusOne(cur, j);
  41. if (!visited.count(up))
  42. {
  43. temp.insert(up);
  44. }
  45. string down = minusOne(cur, j);
  46. if (!visited.count(down))
  47. {
  48. temp.insert(down);
  49. }
  50. }
  51. }
  52. //在这里增加步数
  53. ++step;
  54. s1.swap(temp);
  55. }
  56. //如果穷举完都没有找到目标密码,则返回-1
  57. return -1;
  58. }
  59. string plusOne(const string& s, int j)
  60. {
  61. string tmp(s);
  62. if (tmp[j] == '9')
  63. {
  64. tmp[j] = '0';
  65. }
  66. else
  67. {
  68. tmp[j] += 1;
  69. }
  70. return tmp;
  71. }
  72. string minusOne(const string& s, int j)
  73. {
  74. string tmp(s);
  75. if (tmp[j] == '0')
  76. {
  77. tmp[j] = '9';
  78. }
  79. else
  80. {
  81. tmp[j] -= 1;
  82. }
  83. return tmp;
  84. }
  85. };

2 单词接龙

127. 单词接龙

剑指 Offer II 108. 单词演变

  1. //双向BFS
  2. class Solution {
  3. public:
  4. int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
  5. // 如果目标单词不在 wordList 中,说明无解
  6. if (std::find(wordList.begin(), wordList.end(), endWord) == wordList.end())
  7. return 0;
  8. // 将所有 word 存入 unordered_set
  9. unordered_set<string> s;
  10. for (auto& str : wordList)
  11. s.insert(str);
  12. int ans = bfs(beginWord, endWord, s);
  13. return ans == -1 ? 0 : ans + 1;
  14. }
  15. int bfs(const string& beginWord, const string& endWord, unordered_set<string>& s)
  16. {
  17. // q1 代表从起点 beginWord 开始搜索(正向)
  18. // q2 代表从结尾 endWord 开始搜索(反向)
  19. queue<string> q1, q2;
  20. /*
  21. * m1 和 m2 分别记录两个方向出现的单词是经过多少次转换而来
  22. * e.g.
  23. * m1 = {"abc":1} 代表 abc 由 beginWord 替换 1 次字符而来
  24. * m2 = {"xyz":3} 代表 xyz 由 endWord 替换 3 次字符而来
  25. */
  26. unordered_map<string, int> m1, m2;
  27. q1.push(beginWord); m1.insert({ beginWord, 0 });
  28. q2.push(endWord); m2.insert({ endWord, 0 });
  29. /*
  30. * 只有两个队列都不空,才有必要继续往下搜索
  31. * 如果其中一个队列空了,说明从某个方向搜到底都搜不到该方向的目标节点
  32. * e.g.
  33. * 例如,如果 q1 为空了,说明从 beginWord 搜索到底都搜索不到 endWord,反向搜索也没必要进行了
  34. */
  35. while (!q1.empty() && !q2.empty())
  36. {
  37. int t = -1;
  38. // 为了让两个方向的搜索尽可能平均,优先拓展队列内元素少的方向
  39. if (q1.size() <= q2.size())
  40. {
  41. t = update(q1, m1, m2, s);
  42. }
  43. else
  44. {
  45. t = update(q2, m2, m1, s);
  46. }
  47. if (t != -1)
  48. return t;
  49. }
  50. return -1;
  51. }
  52. // update 代表从 queue 中取出一个单词进行扩展,
  53. // cur 为当前方向的距离字典;other 为另外一个方向的距离字典
  54. int update(queue<string>& queue, unordered_map<string, int>& cur, unordered_map<string, int>& other, unordered_set<string>& s)
  55. {
  56. int m = queue.size();
  57. while (m--)
  58. {
  59. // 获取当前需要扩展的原字符串
  60. string firstStr = queue.front();
  61. queue.pop();
  62. // 枚举替换原字符串中索引为i的字符
  63. for (int i = 0; i < firstStr.length(); ++i)
  64. {
  65. // 枚举将 i 替换成小写字母
  66. for (int j = 0; j < 26; ++j)
  67. {
  68. // 替换后的字符串
  69. string nextStr = firstStr.substr(0, i).append(1, 'a'+j).append(firstStr.substr(i+1));
  70. if (s.count(nextStr))
  71. {
  72. // 如果 nextStr 在「当前方向」被记录过,且被记录的次数小于等于 cur[firstStr] + 1, 加1表示 firstStr -> nextStr
  73. if (cur.count(nextStr) && cur[nextStr] <= cur[firstStr] + 1)
  74. continue;
  75. // 如果该字符串在「另一方向」出现过,说明找到了联通两个方向的最短路
  76. if (other.count(nextStr))
  77. {
  78. return cur[firstStr] + 1 + other[nextStr];
  79. }
  80. else
  81. {
  82. // 否则加入 queue 队列
  83. queue.push(nextStr);
  84. cur.insert({nextStr, cur[firstStr]+1});
  85. }
  86. }
  87. }
  88. }
  89. }
  90. return -1;
  91. }
  92. };

3

4

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/718644
推荐阅读
相关标签
  

闽ICP备14008679号