当前位置:   article > 正文

127单词接龙(广度优先搜索、双向广度优先搜索)_单词接龙广度优先搜索+优化建图c语言完整写法

单词接龙广度优先搜索+优化建图c语言完整写法

1、题目描述

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:

  • 每次转换只能改变一个字母。
  • 转换过程中的中间单词必须是字典中的单词。

说明:

  • 如果不存在这样的转换序列,返回 0。
  • 所有单词具有相同的长度。
  • 所有单词只由小写字母组成。
  • 字典中不存在重复的单词。
  • 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

2、示例

输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

输出: 5

解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
     返回它的长度 5。

3、题解

问题抽象成把所有单词看成结点,相差一个字符的两个单词就是相邻的节点,给定起始节点和终止节点,找最短路径。本质上就是在正常的遍历的基础上,去剪枝,从而提高速度,如果使用简单的递归DFS,当数据达到一定程度超时。

  • 剪枝思想(核心思想):在考虑第 k 层的某一个单词,如果这个单词在第 1 到 k-1 层已经出现过,我们其实就不用继续向下探索了,该序列路径一定不是最短转换序列。

解法一:BFS

基本思想:BFS

  • mindepth用于记录当前单词cur最先出现的层数,dict空间换时间用于查找cur相邻的单词,queue用于层次遍历
  • 先把该层的所有单词记录深度,这时mindepth记录的才是单词最先出现的层数
  • 遍历当前层所有单词,记录每个单词相邻的在之前层没有出现过所有单词入队列queue
  • 查找当前单词cur的下一个转换的单词:和cur只相差一个字母
  • 在考虑第 k 层的某一个单词,如果这个单词在第 1 到 k-1 层已经出现过,我们其实就不用继续向下探索了

解法二:双向BFS

基本思想:双向BFS,两个队列queue1、queue2分别从beginWord和endWord出发查找相邻的单词(这些单词在之前层没有出现过),直到两个队列中遇到相同的单词,至于每次从哪个方向扩展,我们可以每次选择需要扩展的节点数少的方向进行扩展。

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<vector>
  4. #include<unordered_map>
  5. #include<unordered_set>
  6. #include<list>
  7. using namespace std;
  8. class Solution {
  9. public:
  10. int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
  11. //基本思想:BFS
  12. unordered_map<string, int> mindepth; //mindepth用于记录当前单词cur最先出现的层数
  13. unordered_set<string> dict(wordList.begin(), wordList.end()); //空间换时间,用于查找cur相邻的单词
  14. list<string> queue; //queue用于层次遍历
  15. int flag = 0; //标记是否已经遇到endWord
  16. queue.push_front(beginWord);
  17. int depth = 0; //层次遍历的深度
  18. while (!queue.empty())
  19. {
  20. depth++;
  21. int len = queue.size();
  22. //先把该层的所有单词记录深度,这时mindepth记录的才是单词最先出现的层数
  23. for (auto i = queue.begin(); i != queue.end(); i++)
  24. {
  25. if (mindepth.find(*i) == mindepth.end())
  26. mindepth[*i] = depth;
  27. }
  28. //遍历当前层所有单词,记录每个单词相邻的在之前层没有出现过所有单词入队列queue
  29. for (int i = 0; i < len; i++)
  30. {
  31. string cur = queue.back();
  32. queue.pop_back();
  33. if (cur == endWord)
  34. flag = 1;
  35. string temp = cur;
  36. //查找当前单词cur的下一个转换的单词:和cur只相差一个字母
  37. for (int j = 0; j < cur.size(); j++)
  38. {
  39. for (char c = 'a'; c <= 'z'; c++)
  40. {
  41. if (cur[j] == c)
  42. continue;
  43. char oldc = cur[j];
  44. cur[j] = c;
  45. //在考虑第 k 层的某一个单词,如果这个单词在第 1 到 k-1 层已经出现过,我们其实就不用继续向下探索了
  46. if (dict.find(cur) != dict.end() && mindepth.find(cur) == mindepth.end())
  47. {
  48. queue.push_front(cur);
  49. }
  50. cur[j] = oldc;
  51. }
  52. }
  53. }
  54. if (flag == 1)
  55. break;
  56. }
  57. if (flag == 1)
  58. return depth;
  59. else
  60. return 0;
  61. }
  62. };
  63. class Solution1 {
  64. public:
  65. int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
  66. //基本思想:双向BFS,两个队列queue1、queue2分别从beginWord和endWord出发查找相邻的单词(这些单词在之前层没有出现过),直到两个队列中遇到相同的单词
  67. //至于每次从哪个方向扩展,我们可以每次选择需要扩展的节点数少的方向进行扩展
  68. unordered_map<string, int> mindepth1; //mindepth用于记录beginWord往下相邻单词最先出现的层数
  69. unordered_map<string, int> mindepth2; //mindepth用于记录endWord往上相邻单词最先出现的层数
  70. unordered_set<string> dict(wordList.begin(), wordList.end()); //空间换时间,用于查找cur相邻的单词
  71. if (dict.find(endWord) == dict.end())
  72. return 0;
  73. list<string> queue1; //queue1用于beginWord往下层次遍历
  74. list<string> queue2; //queue2用于endWord往上层次遍历
  75. int flag = 0; //标记两个队列是否遇到相同的单词
  76. queue1.push_front(beginWord);
  77. queue2.push_front(endWord);
  78. int depth1 = 0; //beginWord往下层次遍历的深度
  79. int depth2 = 0; //endWord往上层次遍历的深度
  80. while (!queue1.empty() && !queue2.empty())
  81. {
  82. int len1 = queue1.size();
  83. int len2 = queue2.size();
  84. int len;
  85. list<string> queue;
  86. int depth;
  87. unordered_map<string, int> mindepth;
  88. if (len1 <= len2)
  89. {
  90. depth1++;
  91. queue = queue1;
  92. mindepth = mindepth1;
  93. len = len1;
  94. depth = depth1;
  95. }
  96. else
  97. {
  98. depth2++;
  99. queue = queue2;
  100. mindepth = mindepth2;
  101. len = len2;
  102. depth = depth2;
  103. }
  104. //先把该层的所有单词记录深度,这时mindepth记录的才是单词最先出现的层数
  105. for (auto i = queue.begin(); i != queue.end(); i++)
  106. {
  107. if (mindepth.find(*i) == mindepth.end())
  108. mindepth[*i] = depth;
  109. }
  110. //遍历当前层所有单词,记录每个单词相邻的在之前层没有出现过所有单词入队列queue
  111. for (int i = 0; i < len; i++)
  112. {
  113. string cur = queue.back();
  114. queue.pop_back();
  115. string temp = cur;
  116. //查找当前单词cur的下一个转换的单词:和cur只相差一个字母
  117. for (int j = 0; j < cur.size(); j++)
  118. {
  119. for (char c = 'a'; c <= 'z'; c++)
  120. {
  121. if (cur[j] == c)
  122. continue;
  123. char oldc = cur[j];
  124. cur[j] = c;
  125. //在考虑第 k 层的某一个单词,如果这个单词在第 1 到 k-1 层已经出现过,我们其实就不用继续向下探索了
  126. if (dict.find(cur) != dict.end() && mindepth.find(cur) == mindepth.end())
  127. {
  128. queue.push_front(cur);
  129. }
  130. cur[j] = oldc;
  131. }
  132. }
  133. }
  134. if (len1 <= len2)
  135. {
  136. queue1 = queue;
  137. mindepth1 = mindepth;
  138. }
  139. else
  140. {
  141. queue2 = queue;
  142. mindepth2 = mindepth;
  143. }
  144. //直到两个队列中遇到相同的单词flag=1跳出循环
  145. for (auto i = queue1.begin(); i != queue1.end() && flag==0; i++)
  146. {
  147. for (auto j = queue2.begin(); j != queue2.end() && flag==0; j++)
  148. {
  149. if (*i == *j)
  150. flag = 1;
  151. }
  152. }
  153. if (flag == 1)
  154. break;
  155. }
  156. if (flag == 1)
  157. return depth1 + depth2 + 1;
  158. else
  159. return 0;
  160. }
  161. };
  162. int main()
  163. {
  164. Solution1 solute;
  165. string beginWord = "hog";
  166. string endWord = "cog";
  167. vector<string> wordList = { "cog" };
  168. cout << solute.ladderLength(beginWord, endWord, wordList) << endl;
  169. return 0;
  170. }

 

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

闽ICP备14008679号