LeetCode 126. Word Ladder II

问题描述

  • Given two words (beginWord and endWord), and a dictionary’s word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:
    Only one letter can be changed at a time
    Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
  • Note:
    Return an empty list if there is no such transformation sequence.
    All words have the same length.
    All words contain only lowercase alphabetic characters.
    You may assume no duplicates in the word list.
    You may assume beginWord and endWord are non-empty and are not the same.
  • Example:
    这里写图片描述
  • 地址

问题分析

  • LeetCode 127. Word Ladder 的进阶,求从beginWordendWord 的最短路径具体是什么。像这种求具体路径的,那么一定用的的是 DFS, 而又要求最短路径,所以也用到 BFS。所以这是一道 BFS + DFS 的经典难题。
  • 先将beginWord 加入到字符串集中,然后求每一个字符串的邻居,这样便建立了一个图的模型 HashMap<String, List<String>> neighbors
  • 然后对该图利用 BFS 求每一个字符串到 beginWord 的最短路径值。HashMmap<String, Integer> distances
  • 最后 利用 neighborsdistances ,通过 DFS 来求得从beignWordendWord 的所有可能最短路径,注意每一步递归结束后对形成的path进行回溯

  • 后来想了一下,也可以不用建立 distances,直接利用BFS来构建一张图(树)即可,只不过要注意的一点是,每一层的str可能会有相同,所以对 visited的要求有所改变,具体见实现

代码实现

class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        //将beginWord 添加进 list
        wordList.add(beginWord);
        //得到每个字符串的邻居(邻居是指只变化了一个字母的字符串)
        HashMap<String, List<String>> neighbors = getNeightbors(wordList);
        //得到每个字符串到beginWord的距离
        HashMap<String, Integer> distances = getDistances(beginWord, wordList, neighbors);
        //path用来存储一条从 beginWord 到 endWord 的最短路径
        ArrayList<String> path = new ArrayList<>();
        //path结果集
        List<List<String>> res = new ArrayList<>();
        //收集所有从 beginWord 到 endWord 的最短路径
        getShortestPaths(beginWord, endWord, neighbors, distances, path, res);
        return res;
    }

    //得到wordList中每个字符串的邻居(邻居是指只变化了一个字母的字符串)
    public HashMap<String, List<String>> getNeightbors(List<String> wordList) {
        HashSet<String> dict = new HashSet<>(wordList);
        HashMap<String, List<String>> neighbors = new HashMap<>();
        for (int i = 0; i < wordList.size(); i++) {
            String str = wordList.get(i);
            neighbors.put(str, new ArrayList<>());
            char[] chs = str.toCharArray();
            for (int j = 0; j < chs.length; j++) {
                for (char ch = 'a'; ch <= 'z'; ch++) {
                    if (ch != chs[j]) {
                        char tmp = chs[j];
                        chs[j] = ch;
                        String strChanged = new String(chs);
                        if (dict.contains(strChanged)) {
                            neighbors.get(str).add(strChanged);
                        }
                        chs[j] = tmp;
                    }
                }
            }

        }
        return neighbors;
    }

    //利用BFS来计算每个字符串到beginWord的距离
    public HashMap<String, Integer> getDistances(String beginWord, List<String> wordList, HashMap<String, List<String>> neighbors) {
        HashSet<String> visited = new HashSet<>();
        HashMap<String, Integer> distances = new HashMap<>();
        LinkedList<String> queue = new LinkedList<>();
        //初始化
        distances.put(beginWord, 0);
        queue.add(beginWord);
        visited.add(beginWord);
        while (! queue.isEmpty()) {
            String cur = queue.poll();
            for (String str : neighbors.get(cur)) {
                if (!visited.contains(str)) {
                    //又一种找距离(层数)的方法,因为用map记录了上一层的距离,所以可以这样左
                    distances.put(str, distances.get(cur) + 1);
                    queue.add(str);
                    visited.add(str);
                }
            }
        }
        return distances;
    }

    //利用 DFS 来收集所有从 beginWord 到 endWord 的最短路径
    public void getShortestPaths(String beginWord, String endWord, HashMap<String, List<String>> neighbors, 
                                 HashMap<String, Integer> distances, List<String> path, List<List<String>> res) {
        //将当前添加进path
        path.add(beginWord);
        if (beginWord.equals(endWord)) {
            //如果已经变化到endWord, 那么将当前路径的一份copy添加进结果集
            res.add(new ArrayList<>(path));
        }else {
            for (String neigbor : neighbors.get(beginWord)) { 
                //继续dfs,从beginWord的所有邻居节点,并且是属于最短路径中该节点的邻居节点开始dfs
                if (distances.get(beginWord) + 1 == distances.get(neigbor)) {
                    getShortestPaths(neigbor, endWord, neighbors, distances, path, res);
                }
            }
        }
        //用于回溯!!!很重要!!!
        path.remove(path.size() - 1);
    }
}
  • 后来想法(慢,但是不麻烦):
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
       // wordList.add(beginWord);
        HashMap<String, List<String>> neighbors = getNeightbors(beginWord, wordList);
        ArrayList<String> path = new ArrayList<>();
        //path结果集
        List<List<String>> res = new ArrayList<>();
        //收集所有从 beginWord 到 endWord 的最短路径
        getShortestPaths(beginWord, endWord, neighbors, path, res);
        return res;
    }

    public HashMap<String, List<String>> getNeightbors(String beginWord, List<String> wordList) {
        HashSet<String> set = new HashSet<>(wordList);
        HashSet<String> visited = new HashSet<>();
        HashMap<String, List<String>> neighbors = new HashMap<>();
        LinkedList<String> queue = new LinkedList<>();
        queue.add(beginWord);
        visited.add(beginWord);
        while (! queue.isEmpty()) {
            int levelSize = queue.size();
            ArrayList<String> levelStr = new ArrayList<>();
            //以每一层为单位构建一个图(树),每一层中的孩子节点可以重复 
            //比如该层 有 hit 与 hog 那么下一层都可以添加 hot 作为neigbor,所以当前层不计重复
            //负责最后到endWord的时候,只能作为一个str的neighbor,肯定是不对的
            for (int count = 0; count < levelSize; count++) {
                String str = queue.poll();
                neighbors.put(str, new ArrayList<>());
                char[] chs = str.toCharArray();
                for (int i = 0; i < chs.length; i++) {
                    char tmp = chs[i];
                    for (char ch = 'a'; ch <= 'z'; ch++) {
                        if (ch != chs[i]) {
                            chs[i] = ch;
                            String strChange = new String(chs);
                            if (set.contains(strChange) && ! visited.contains(strChange)) {
                                neighbors.get(str).add(strChange);
                                queue.add(strChange);
                                levelStr.add(strChange);
                            }
                            chs[i] = tmp;
                        }
                    }
                }
            }
            //这步骤比较超时
            visited.addAll(levelStr);
        }
        return neighbors;
    }


    public void getShortestPaths(String beginWord, String endWord, HashMap<String, List<String>> neighbors, List<String> path,                                                                              List<List<String>> res) {
        path.add(beginWord);
        if (beginWord.equals(endWord)) {
            res.add(new ArrayList<>(path));
        }else {
           for (String neighbor : neighbors.get(beginWord)) {
               getShortestPaths(neighbor, endWord, neighbors, path, res);
           } 
        }
        path.remove(path.size() - 1);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值