当前位置:   article > 正文

算法--单词接龙(BFS)_算法单词接龙

算法单词接龙

字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:

序列中第一个单词是 beginWord 。
序列中最后一个单词是 endWord 。
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典 wordList 中的单词。
给你两个单词 beginWord 和 endWord 和一个字典 wordList ,找到从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。

https://leetcode-cn.com/problems/word-ladder/

最短转换序列长度--广度优先搜索

1.广度优先搜索

把每个单词都抽象为一个点,如果两个单词可以只改变一个字母进行转换,那么说明他们之间有一条双向边。因此我们只需要把满足转换条件的点相连,就形成了一张图。

以 beginWord 为图的起点,以 endWord 为终点进行广度优先搜索,寻找 beginWord 到 endWord 的最短路径。

fig1

将给定单词集合和开始节点构建一张图,从开始节点到指定结束节点搜索最短路径。

利用哈希表来存储单词和单词编号的关系

利用List<List<Integer>>列表来存储每个单词与其相邻单词的关系,理论上只有当两个单词只有一个字母不同时,在图中会存在一条边,这里为了便于简化单词边关系的判断逻辑,增加一些虚拟节点,从每个单词触发,依次仅改变每一个字母来得到一个当前单词的相邻单词集合,变为该单词的边关系。

  1. // 保存单词和编号的关系
  2. Map<String, Integer> wordId = new HashMap<>();
  3. // 保存单词与相邻节点的关系
  4. List<List<Integer>> edge = new ArrayList<>();
  5. // 单词数量计数
  6. int nodeNum = 0;
  7. public void addWord(String word) {
  8. if (!wordId.containsKey(word)) {
  9. wordId.put(word, nodeNum++);
  10. edge.add(new ArrayList<>());
  11. }
  12. }
  13. public void addEdge(String word) {
  14. addWord(word);
  15. int id1 = wordId.get(word);
  16. char[] array = word.toCharArray();
  17. for (int i=0; i<array.length; i++) {
  18. char tmp = array[i];
  19. array[i] = '*';
  20. String newWord = new String(array);
  21. addWord(newWord);
  22. int id2 = wordId.get(newWord);
  23. edge.get(id1).add(id2);
  24. edge.get(id2).add(id1);
  25. array[i] = tmp;
  26. }
  27. }
  28. public int ladderLength(String beginWord, String endWord, List<String> wordList) {
  29. // 将所有单词添加进图中
  30. wordList.forEach(this::addEdge);
  31. // 将开始单词添加进图中
  32. addEdge(beginWord);
  33. if (!wordId.containsKey(endWord)) return 0;
  34. int[] dis = new int[nodeNum];
  35. Arrays.fill(dis, Integer.MAX_VALUE);
  36. int beginId = wordId.get(beginWord);
  37. int endId = wordId.get(endWord);
  38. // 记录从单词i开始到达结束单词的路径 起始单词初始化为0 其余初始化为最大值
  39. dis[beginId] = 0;
  40. Queue<Integer> queue = new LinkedList<>();
  41. // 从开始单词开始搜索
  42. queue.add(beginId);
  43. while (!queue.isEmpty()) {
  44. int x = queue.poll();
  45. // 如果当前单词等于结束单词 已经找到 返回
  46. if (x == endId) return dis[endId] / 2 + 1;
  47. // 否则 找出该单词所有可能到达的路径
  48. for (int it: edge.get(x)) {
  49. // 如果等于最大值 表示还没有遍历过
  50. if (dis[it] == Integer.MAX_VALUE) {
  51. // 单词x到达结束单词路径为dis[x]
  52. // 单词it比单词x距离大一
  53. dis[it] = dis[x] + 1;
  54. queue.add(it);
  55. }
  56. }
  57. }
  58. return 0;
  59. }

 

2.双向优先搜索

构建图关系之后,从开始节点和结束节点开始,依次BFS遍历寻找,如果开始和结束都遍历过同一个节点,则说明最短路径已经找到。

  1. // 双向广度优先搜索
  2. public int ladderLength2(String beginWord, String endWord, List<String> wordList) {
  3. wordList.forEach(this::addEdge);
  4. addEdge(beginWord);
  5. if (!wordId.containsKey(endWord)) return 0;
  6. int[] disBegin = new int[nodeNum];
  7. Arrays.fill(disBegin, Integer.MAX_VALUE);
  8. int beginId = wordId.get(beginWord);
  9. disBegin[beginId] = 0;
  10. Queue<Integer> queBegin = new LinkedList<>();
  11. queBegin.add(beginId);
  12. int[] disEnd = new int[nodeNum];
  13. Arrays.fill(disEnd, Integer.MAX_VALUE);
  14. int endId = wordId.get(endWord);
  15. disEnd[endId] = 0;
  16. Queue<Integer> queEnd = new LinkedList<>();
  17. queEnd.add(endId);
  18. while (!queBegin.isEmpty() && !queEnd.isEmpty()) {
  19. // 从开始节点开始BFS
  20. int queBeginSize = queBegin.size();
  21. for (int i=0; i<queBeginSize; i++) {
  22. int nodeBegin = queBegin.poll();
  23. // 如果开始节点在结束队列中已经搜索过 则直接返回
  24. if (disEnd[nodeBegin] != Integer.MAX_VALUE) {
  25. return (disBegin[nodeBegin] + disEnd[nodeBegin]) / 2 + 1;
  26. }
  27. // 遍历当前节点的所有相邻节点 更新距离
  28. for (int it: edge.get(nodeBegin)) {
  29. if (disBegin[it] == Integer.MAX_VALUE) {
  30. disBegin[it] = disBegin[nodeBegin] + 1;
  31. queBegin.add(it);
  32. }
  33. }
  34. }
  35. // 开始节点完成 尝试从结束节点开始BFS
  36. int queEndSize = queEnd.size();
  37. for (int i=0; i<queEndSize; i++) {
  38. int nodeEnd = queEnd.poll();
  39. // 如果结束节点在开始队列中已经搜索过 则直接返回
  40. if (disBegin[nodeEnd] != Integer.MAX_VALUE) {
  41. return (disBegin[nodeEnd] + disEnd[nodeEnd]) / 2 + 1;
  42. }
  43. // 遍历当前节点的所有相邻节点 更新距离
  44. for (int it: edge.get(nodeEnd)) {
  45. if (disEnd[it] == Integer.MAX_VALUE) {
  46. disEnd[it] = disEnd[nodeEnd] + 1;
  47. queEnd.add(it);
  48. }
  49. }
  50. }
  51. }
  52. return 0;
  53. }

参考地址:https://leetcode-cn.com/problems/word-ladder/solution/dan-ci-jie-long-by-leetcode-solution/

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

闽ICP备14008679号