【LeetCode】解题126:Word Ladder II(BFS算法)

使用BFS解决LeetCode 126题:Word Ladder II
本文介绍了如何利用BFS算法解决LeetCode中的126题——从`beginWord`到`endWord`的最短变换序列,确保每次仅更改一个字母且目标单词存在于字典`wordList`中。当`endWord`不在字典中时,返回空列表。文中详细解释了解题思路,包括如何剪枝以优化搜索效率,并提供了Java实现的解决方案,重点优化了查找和字符串转换操作,以降低时间复杂度。

LeetCode解题 126:Word Ladder II(BFS算法)

Problem 126: Word Ladder II [Hard]

Given two words (beginWord and endWord), and a dictionary’s word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:

  1. Only one letter can be changed at a time
  2. 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 1:

Input:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]

Output:
[
[“hit”,“hot”,“dot”,“dog”,“cog”],
[“hit”,“hot”,“lot”,“log”,“cog”]
]

Example 2:

Input:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”]

Output: []

Explanation: The endWord “cog” is not in wordList, therefore no possible transformation.

来源:LeetCode

解题思路

因为题中要求最短路径,更适合使用BFS算法求解该题,在某一层搜索到endWord后即可停止搜索。

具体思路:

  • 首先查找wordList中是否含有endWord,如果没有,直接返回空。
  • 然后使用bfs搜索,队列prePaths存储到当前层的所有路径,依次推出每个路径进行下一层搜索:
    a. 找到每个路径的最末单词lastWord;
    b. 使用transform()函数返回lastWord的所有分支(即只改变一位字符就能到达的单词);
    c. 遍历所有分支word:
    i. 如果在之前层已经出现过( w o r d ∈ S e t < > p r e W o r d word \in Set<> preWord wordSet<>preWord),那么可以直接剪枝,因为一定不会是最短路径;
    ii. 如果word = endWord,可以把path加入最终结果,并将剩余的分支word直接剪枝,因为当前父节点已经到达了最终单词,没必要再遍历剩余分支,并且等到当前层全部遍历完后可以停止搜索;
    iii. 如果没有以上情况,word加入path后把path加入队列prePaths。
  • 当前层搜索结束后,如果已经出现endWord,直接停止搜索返回结果;如果没有还没搜索到endWord,继续搜索下一层。

假设从beginWord到endWord需要长度d,每个word分支为k,则时间复杂度为 O ( k d ) O(k^d) O(kd)

运行结果:
在这里插入图片描述
要点:bfs剪枝

Solution (Java)

class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        List<List<String>> result = new ArrayList<List<String>>();
        Set<String> dictionary =  new HashSet<String>(wordList);
        if(!dictionary.contains(endWord)) return result;
        bfs(beginWord, endWord, dictionary, result);
        return result;
    }
    private void bfs(String beginWord, String endWord, Set<String> wordList, List<List<String>> result){
        Set<String> preWord = new HashSet<String>();
        preWord.add(beginWord);
        Queue<List<String>> prePaths = new LinkedList<List<String>>();
        List<String> begin = new ArrayList<String>();
        begin.add(beginWord);
        prePaths.offer(begin);
        boolean find = false;
        while(!prePaths.isEmpty()){
            Set<String> peerWord = new HashSet<String>();
            int size = prePaths.size();
            for(int i = 0; i < size; i++){
                List<String> path = prePaths.poll();
                String lastWord = path.get(path.size() - 1); // the last word of the path
                List<String> branch = transform(lastWord, wordList);
                for(String word : branch){
                    if(preWord.contains(word)) continue; // pruning
                    path.add(word);
                    prePaths.offer(new ArrayList<String>(path));
                    peerWord.add(word);
                    if(word.equals(endWord)){
                        find = true;
                        result.add(new ArrayList<String>(path));
                        break; // pruning
                    }
                    path.remove(path.size() - 1);
                }
            }
            preWord.addAll(peerWord);
            if(find){
                break; // finish search
            }
        }
    }
    // all transformed word
    private List<String> transform(String word, Set<String> wordList){
        List<String> nextWord = new ArrayList<String>();
        char[] s = word.toCharArray();
        for(int i = 0; i < s.length; i++){
            for(char c = 'a'; c <= 'z'; c++){
                if(s[i] == c) continue;
                char oldch = s[i];
                s[i] = c;
                if(wordList.contains(String.valueOf(s))){
                    nextWord.add(String.valueOf(s));
                }
                s[i] = oldch;
            }
        }
        return nextWord;
    }
    
}

修改过程

  • 一开始直接使用List的contains()函数,会时间超限,将List<String> wordList转化为HashSet减少用时能够AC。
  • transform()函数中原本使用String的子串拼接修改位置i上的字符,发现需要耗费大量时间,后将String word转换为char[] s后再修改s[i]会快一倍的时间。
  • transform()函数的写法参考LeetCode题解,使用的方法为分别用a~z替换单词的每个位置,查看是否在wordList中,这种方法比wordList中的单词一一按位比较是否只差一位更快一些。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值