剑指 Offer II 108. 单词演变(困难 图 bfs 哈希表 字符串)

该博客讨论了一种图论问题的解决方案,即寻找两个单词间的最短转换序列。通过将单词看作图的节点,用单向和双向广度优先搜索(BFS)策略来查找从起始单词到目标单词的最短路径。在单向BFS中,使用两个队列交替搜索;而在双向BFS中,同时从起始和目标单词开始搜索,减少搜索空间,提高效率。博客内容涵盖了算法分析、代码实现和样例解释,展示了如何在给定字典中找到两个单词间的最短转换序列。
摘要由CSDN通过智能技术生成

剑指 Offer II 108. 单词演变

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

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

示例 1:

输入:beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
输出:5
解释:一个最短转换序列是 “hit” -> “hot” -> “dot” -> “dog” -> “cog”, 返回它的长度 5。
示例 2:

输入:beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
输出:0
解释:endWord “cog” 不在字典中,所以无法进行转换。

提示:

1 <= beginWord.length <= 10
endWord.length == beginWord.length
1 <= wordList.length <= 5000
wordList[i].length == beginWord.length
beginWord、endWord 和 wordList[i] 由小写英文字母组成
beginWord != endWord
wordList 中的所有字符串 互不相同

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/om3reC
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析

将本题转变为图的题:数组中每个单词代表图的节点,每个节点的邻接点是该节点更换了一个字母的其他单词,转换完成后,那么本题演变为求图的最短路径问题,用 bfs 求解。
方法一:单向 bfs
为了求两个节点之间的最短距离,用两个队列实现 bfs,一个队列 q1 中存放离起始节点距离为 d 的节点,当从这个队列中取出节点并访问时,与队列 q1 中节点相邻的节点离起始节点的距离都是 d + 1,将这些相邻的节点存放到另一个队列 q2 中。当 q1 中的节点都访问完的时候,再访问队列 q2 的节点,并将相邻的节点放入 q1 中,可以交替使用 q1 和 q2 由近及远地从起始节点开始搜索所有节点。
获取邻接点的方式用每次更换一个字母的方法存储所有可能的单词。
方法二:双向 bfs
上面的方法是按照从起始点出发不断朝着目标节点的方向搜索,直到到达目标节点。针对这类问题有种常见的优化方法,即在从起始节点出发不断朝着目标节点的方向搜索的同时,也从目标节点出发不断朝着起始节点的方向搜索。这种双向搜索的方法能够缩小搜索空间,从而提高搜索的时间效率。
对于上述的两个方向上当前要访问的节点用两个哈希表 set1 和 set2 存储,再创建一个哈希表 set3 来存储与当前访问的节点相邻的节点,用哈希表而不用队列的原因是,要判断从一个方向搜索到的节点在另一个方向是否已经访问过,只需要O(1)的时间就能判断 HashSet 种是否包含一个元素。

题解(Java)

方法一:单向 bfs

class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        Queue<String> q1 = new LinkedList<>();
        Queue<String> q2 = new LinkedList<>();
        Set<String> notVisited = new HashSet<>(wordList);
        int length = 1;
        q1.offer(beginWord);
        while (!q1.isEmpty()) {
            String word = q1.poll();
            if (word.equals(endWord)) {
                return length;
            }

            List<String> neighbors = getNeighbors(word);
            for (String neighbor : neighbors) {
                if (notVisited.contains(neighbor)) {
                    q2.offer(neighbor);
                    notVisited.remove(neighbor);
                }
            }

            if (q1.isEmpty()) {
                length++;
                q1 = q2;
                q2 = new LinkedList<>();
            }
        }
        return 0;
    }
	//存储所有相邻的单词
    private List<String> getNeighbors(String word) {
        List<String> neighbors = new LinkedList<>();
        char[] chars = word.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            char old = chars[i];
            for (char ch = 'a'; ch <= 'z'; ch++) {
                if (old != ch) {
                    chars[i] = ch;
                    neighbors.add(new String(chars));
                }
            }
            chars[i] = old;
        }
        return neighbors;
    }
}

方法二:双向 bfs

class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        Set<String> notVisited = new HashSet<>(wordList);
        if (!notVisited.contains(endWord)) return 0;
        Set<String> set1 = new HashSet<>();
        Set<String> set2 = new HashSet<>();
        set1.add(beginWord);
        set2.add(endWord);
        int length = 2;
        while (!set1.isEmpty() && !set2.isEmpty()) {
            if (set2.size() <set1.size()) { //从访问节点少的方向走
                Set<String> temp = set1;
                set1 = set2;
                set2 = temp;
            }

            Set<String> set3 = new HashSet<>(); //存储与当前访问的节点相邻的节点
            for (String word : set1) {
                List<String> neighbors = getNeighbors(word);
                for (String neighbor : neighbors) {
                    if (set2.contains(neighbor)) {
                        return length;
                    }

                    if (notVisited.contains(neighbor)) {
                        set3.add(neighbor);
                        notVisited.remove(neighbor);
                    }
                }
            }

            length++;
            set1 = set3;
        }
        return 0;
    }

    private List<String> getNeighbors(String word) {
        List<String> neighbors = new LinkedList<>();
        char[] chars = word.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            char old = chars[i];
            for (char ch = 'a'; ch <= 'z'; ch++) {
                if (old != ch) {
                    chars[i] = ch;
                    neighbors.add(new String(chars));
                }
            }
            chars[i] = old;
        }
        return neighbors;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值