127.单词接龙(双向DFS)

QQ截图20210627162306.png

  1. Array.prototype.isEmpty = function () {
  2. return this.length === 0 ? true : false;
  3. }
  4. /**
  5. * @param {string} beginWord
  6. * @param {string} endWord
  7. * @param {string[]} wordList
  8. * @return {number}
  9. */
  10. var ladderLength = function (beginWord, endWord, wordList) {
  11. let set = new Set();
  12. // 将wordList保存在集合中
  13. for (const w of wordList) {
  14. set.add(w);
  15. }
  16. if (!set.has(endWord))
  17. return 0;
  18. let ans = bfs();
  19. return ans === -1 ? 0 : ans + 1;
  20. function bfs() {
  21. // d1代表从起点beginWord开始搜索(正向)
  22. // d2代表从结尾endWord开始搜索(反向)
  23. let d1 = new Array();
  24. let d2 = new Array();
  25. /**
  26. * m1和m2分别记录两个方向出现的单词是经过多少次转换而来
  27. * eg:
  28. * m1 = {"abc":1} 代表 abc 由 beginWord 替换 1 次字符而来
  29. * m2 = {"xyz":3} 代表 xyz 由 endWord 替换 3 次字符而来
  30. */
  31. let m1 = new Map();
  32. let m2 = new Map();
  33. d1.push(beginWord);
  34. m1.set(beginWord, 0);
  35. d2.push(endWord);
  36. m2.set(endWord, 0);
  37. /*
  38. * 只有两个队列都不空,才有必要继续往下搜索
  39. * 如果其中一个队列空了,说明从某个方向搜到底都搜不到该方向的目标节点
  40. * eg:
  41. * 例如,如果 d1 为空了,说明从 beginWord 搜索到底都搜索不到 endWord,反向搜索也没必要进行了
  42. */
  43. while (!d1.isEmpty() && !d2.isEmpty()) {
  44. let t = -1;
  45. // 为了平衡两个方向搜索的程度,通过队列长度来确定执行哪个
  46. if (d1.length <= d2.length) {
  47. // 更新队列d1
  48. t = update(d1, m1, m2);
  49. } else {
  50. // 更新队列d2
  51. t = update(d2, m2, m1);
  52. }
  53. if (t != -1) {
  54. return t;
  55. }
  56. }
  57. return -1;
  58. }
  59. /**
  60. * 从queue中取出一个单词进行扩展,
  61. * @param {Array} queue 当前方向队列
  62. * @param {Map} cur 当前方向的距离字典
  63. * @param {*} other 另外一个方向的距离字典
  64. * @returns 转换次数
  65. */
  66. function update(queue, cur, other) {
  67. // 获取当前需要扩展的字符串
  68. const curWord = queue.shift();
  69. let n = curWord.length;
  70. for (let i = 0; i < n; i++) {
  71. for (let j = 0; j < 26; j++) {
  72. // 将字符串第i个元素更新,更新范围是a~z,是26个
  73. const newChar = String.fromCharCode('a'.charCodeAt() + j);
  74. const nextWord = curWord.substring(0, i) + newChar + curWord.substring(i + 1);
  75. if (set.has(nextWord)) {
  76. // 如果该字符在当前方向扩展过,就跳过
  77. if (cur.has(nextWord))
  78. continue;
  79. // 如果在另一个方向出现过,说明找到了连通两个方向的最短路径
  80. if (other.has(nextWord)) {
  81. return cur.get(curWord) + 1 + other.get(nextWord);
  82. } else {
  83. // 没有出现的话就加入到这个方向的队列中
  84. queue.push(nextWord);
  85. cur.set(nextWord, cur.get(curWord) + 1);
  86. }
  87. }
  88. }
  89. }
  90. return -1;
  91. }
  92. };