【Leetcode】126. 单词接龙 II
最开始用回溯法尝试,通过了2/3的用例,后来出现超时,应该是对于从beginword到endword找不到路径的情况下无法退出循环。
该题一个单词转换为另一个单词的过程,可以抽象成在一个无向图游走,能够转化的两个单词之间形成一条无向边。然后就是求最短路径的问题了。
广度优先搜索一层一层地进行遍历,每层遍历都是以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。
每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 最优解 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径,无权图是指从一个节点到另一个节点的代价都记为 1。
BFS(在一般的带权图中是不能解决最短路问题,因为BFS是根据节点到源节点之间的节点数遍历的,也就是先访问离源节点节点数最少的点。要使得BFS能计算最短路径,需要图结构满足所有的权值相等。否则应该使用dijkstra算法去解决最短路。
在程序实现 BFS 时需要考虑以下问题:
- 队列:用来存储每一轮遍历得到的节点;
- 标记:对于遍历过的节点,应该将它标记,防止重复遍历。
class Solution { private List<List<String>> ans = new ArrayList<>(); private Map<String, Integer> wordId = new HashMap<>();//word的索引 private ArrayList<String> idWord = new ArrayList<>();//通过索引找word private ArrayList<Integer>[] edges;//每个节点的边上的另一个节点 public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) { //图的初始化 int id = 0; for(String word:wordList){ if(!wordId.containsKey(word)){ wordId.put(word,id++); idWord.add(word); } } if(!wordId.containsKey(endWord)){ return ans; } if(!wordId.containsKey(beginWord)){ wordId.put(beginWord,id++); idWord.add(beginWord); } edges = new ArrayList[idWord.size()]; for(int i = 0; i < edges.length; i++){ edges[i] = new ArrayList<>(); } for(int i = 0; i < idWord.size(); i++){ for(int j = i+1; j < idWord.size(); j++){ if(isOneDifferent(idWord.get(i),idWord.get(j))){ edges[i].add(j); edges[j].add(i); } } } int dest = wordId.get(endWord); int cost[] = new int[id]; for(int i = 0; i < id; i++){ cost[i] = Integer.MAX_VALUE; } //BFS搜索 Queue<ArrayList<Integer>> q = new LinkedList<>();//存储每一轮得到的节点 ArrayList<Integer> tmpBegin = new ArrayList<>(); tmpBegin.add(wordId.get(beginWord)); q.add(tmpBegin); cost[wordId.get(beginWord)] = 0; while(!q.isEmpty()){ ArrayList<Integer> cur = q.poll(); int last = cur.get(cur.size()-1); if(last == wordId.get(endWord)){//找到一条最短路径 ArrayList<String> tmp = new ArrayList<>(); for(int index : cur){ tmp.add(idWord.get(index));//索引查找单词 } ans.add(tmp);//记录结果 }else{//寻找到endword的路径 for(int i = 0; i < edges[last].size();i++){//遍历last的边 int to = edges[last].get(i);//last边的另一个节点 if(cost[last] + 1 <= cost[to]){//可以经由last从起点走到to cost[to] = cost[last] + 1;//起点->to的路径长度更新 ArrayList<Integer> tmp = new ArrayList<>(cur); tmp.add(to); q.add(tmp); } } } } return ans; } private boolean isOneDifferent(String word1, String word2){ int i = 0, j = 0, count = 0; while(i < word1.length() && j < word2.length()){ if (word1.charAt(i) != word2.charAt(j)){ count++; } i++; j++; } return count == 1 && i == word1.length() && j == word2.length(); } }