题目

image.png
image.png

题解

已知条件:

  • 相邻两个单词只会有单个字母不同 邻里关系
  • 必须从执行开始单词开始
  • 结尾必须是指定的结束单词

邻里关系表
image.png
将邻里关系转换成图
image.png
分层示意图
image.png
第一步使用bfs建立图中的邻里关系, 建立单词与层级之间的关系
第二步使用dfs + 回溯 找出最短路径
代码

  1. /**
  2. * 单词接龙II
  3. * @param {*} beginWord
  4. * @param {*} endWord
  5. * @param {*} wordList
  6. * 先构建图,将关系图构建出来
  7. */
  8. var findLadders = function (beginWord, endWord, wordList) {
  9. const wordSet = new Set(wordList);
  10. wordSet.add(beginWord);
  11. if (!wordSet.has(endWord)) {
  12. return [];
  13. }
  14. // 通过bfs将邻里关系与图制作出来
  15. // 单词与层级之间的关系
  16. const wordLevel = new Map();
  17. // 存放邻里关系 key:新单词,value: 父级单词
  18. const relationMap = new Map();
  19. // 访问过的单词列表
  20. const visitWordSet = new Set();
  21. // 单词队列
  22. const wordQueue = [beginWord];
  23. visitWordSet.add(beginWord);
  24. // 是否找到结束单词
  25. let isEnd = false;
  26. // 当前层级
  27. let level = 0;
  28. wordLevel.set(beginWord, 0);
  29. while (wordQueue.length) {
  30. const len = wordQueue.length;
  31. level++;
  32. // 遍历队列
  33. for (let i = 0; i < len; i++) {
  34. const word = wordQueue.shift();
  35. // 遍历单词的字母
  36. for (let l = 0; l < word.length; l++) {
  37. // 遍历26个字母
  38. for (let c = 97; c <= 122; c++) {
  39. // 生成新的单词
  40. const newWord =
  41. word.slice(0, l) + String.fromCharCode(c) + word.slice(l + 1);
  42. // 单词列表是否含有该单词
  43. if (!wordSet.has(newWord)) {
  44. continue;
  45. }
  46. // 单词关系列表是否含有
  47. if (relationMap.has(newWord)) {
  48. relationMap.get(newWord).push(word);
  49. } else {
  50. relationMap.set(newWord, [word]);
  51. }
  52. // 是否访问过
  53. if (visitWordSet.has(newWord)) {
  54. continue;
  55. }
  56. // 是否为结束单词
  57. if (newWord == endWord) {
  58. isEnd = true;
  59. }
  60. // 储存单词与层级关系
  61. wordLevel.set(newWord, level);
  62. // 将新单词添加进访问队列
  63. wordQueue.push(newWord);
  64. // 添加到访问列表
  65. visitWordSet.add(newWord);
  66. }
  67. }
  68. }
  69. }
  70. // 存放结果
  71. const result = [];
  72. /**
  73. * 深度优先搜索
  74. * @param {*} arr 初始数组
  75. * @param {*} word 当前单词
  76. * @param {*} endWord 结束单词
  77. * @returns
  78. */
  79. const dfs = (arr = [], word, endWord) => {
  80. // 递归出口
  81. if (endWord === word) {
  82. result.push([...arr, endWord]);
  83. return;
  84. }
  85. arr.push(word);
  86. //是否有邻里关系
  87. if (relationMap.has(word)) {
  88. // 获取邻里关系的数组
  89. const realationArr = relationMap.get(word);
  90. // 遍历邻里关系的数组
  91. for (let i = 0; i < realationArr.length; i++) {
  92. // 判断那个邻里关系的单词是当前单词的下一层
  93. if (wordLevel.get(word) + 1 === wordLevel.get(realationArr[i])) {
  94. // 递归
  95. dfs(arr, realationArr[i], endWord);
  96. }
  97. }
  98. }
  99. arr.pop(); // 回溯
  100. };
  101. dfs([], beginWord, endWord);// dfs
  102. return result;
  103. };