【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();
    }
}    

 

posted @ 2020-06-07 11:53  xd会飞的猫  阅读(170)  评论(0编辑  收藏  举报