LeetCode题练习与总结:单词接龙--127

一、题目描述

字典 wordList 中从单词 beginWordendWord转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk

  • 每一对相邻的单词只差一个字母。
  • 对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord不需要在 wordList 中。
  • sk == endWord

给你两个单词beginWordendWord 和一个字典 wordList ,返回 beginWordendWord最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 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
  • beginWordendWordwordList[i] 由小写英文字母组成
  • beginWord != endWord
  • wordList 中的所有字符串 互不相同

二、解题思路

  1. 将单词列表转换为图 :首先,我们需要构建一个图,其中包含 wordList 中的所有单词。这可以通过比较每对单词并检查它们是否只有一个字符不同来实现。

  2. 广度优先搜索 :从 beginWord 开始,进行广度优先搜索,寻找 endWord。在搜索过程中,我们记录从 beginWord 到当前单词的路径长度。

  3. 终止条件 :一旦我们找到了 endWord,我们就知道我们已经找到了最短的转换序列,并且可以返回当前的路径长度。

  4. 优化 :为了提高搜索效率,我们可以在开始 BFS 之前预处理 wordList,创建一个通用状态映射,这样我们可以快速找到所有与给定单词只有一个字符差异的单词。

三、具体代码

java 复制代码
import java.util.*;

public class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        // 将单词列表转换为集合,便于快速查找
        Set<String> wordSet = new HashSet<>(wordList);
        if (!wordSet.contains(endWord)) return 0;

        // BFS 队列
        Queue<String> queue = new LinkedList<>();
        queue.offer(beginWord);

        // 记录已访问的单词
        Set<String> visited = new HashSet<>();
        visited.add(beginWord);

        // 记录转换序列的长度
        int level = 1;

        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                String currentWord = queue.poll();
                // 如果找到了结束单词
                if (currentWord.equals(endWord)) {
                    return level;
                }
                // 遍历当前单词的所有可能转换
                for (String nextWord : getNextWords(currentWord, wordSet)) {
                    if (!visited.contains(nextWord)) {
                        queue.offer(nextWord);
                        visited.add(nextWord);
                    }
                }
            }
            level++;
        }
        return 0;
    }

    // 获取当前单词的所有可能转换
    private List<String> getNextWords(String word, Set<String> wordSet) {
        List<String> nextWords = new ArrayList<>();
        for (char ch = 'a'; ch <= 'z'; ch++) {
            for (int i = 0; i < word.length(); i++) {
                // 生成下一个可能的单词
                String nextWord = replace(word, i, ch);
                if (wordSet.contains(nextWord)) {
                    nextWords.add(nextWord);
                }
            }
        }
        return nextWords;
    }

    // 替换单词中的某个字符
    private String replace(String word, int index, char ch) {
        char[] chars = word.toCharArray();
        chars[index] = ch;
        return new String(chars);
    }
}

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 构建 wordSet :将 wordList 转换为 HashSet 的时间复杂度是 O(N),其中 N 是 wordList 的长度。

  • BFS 遍历:在 BFS 过程中,每个单词都可能生成 26 * L 个新单词,其中 L 是单词的长度。在最坏的情况下,所有单词都可能被访问一次。因此,时间复杂度是 O(N * 26 * L)。

  • getNextWords 函数:对于每个单词,我们检查 26 * L 个可能的转换。这个操作的时间复杂度是 O(26 * L)。

  • replace 函数:这个函数的时间复杂度是 O(L),因为我们只是替换了一个字符并生成了一个新的字符串。

  • 综上所述,总的时间复杂度是 O(N * 26 * L)。

2. 空间复杂度
  • wordSetvisited :这两个 HashSet 最多存储 N 个单词,因此它们的空间复杂度是 O(N)。

  • 队列 queue:在 BFS 过程中,队列中最多可能存储 N 个单词,因此空间复杂度是 O(N)。

  • nextWords 列表 :在 getNextWords 函数中,这个列表最多可能存储 26 * L 个单词,因此空间复杂度是 O(26 * L)。

  • 其他变量 :如 level 和循环中的临时变量等,它们的空间复杂度是 O(1)。

  • 综上所述,总的空间复杂度是 O(N + 26 * L)。

综合来看,这个算法的时间复杂度是 O(N * 26 * L),空间复杂度是 O(N + 26 * L)。

五、总结知识点

  1. 集合的使用HashSet 用于存储不重复的元素,并提供了快速的查找、添加和删除操作。代码中使用了 HashSet 来存储 wordList 和已访问的单词。

  2. 队列的使用LinkedList 作为队列使用,实现了先进先出的数据结构。在 BFS 过程中,队列用于存储待访问的单词。

  3. 广度优先搜索(BFS) :这是一种图遍历算法,用于在图中找到最短路径。代码中使用了 BFS 来找到从 beginWordendWord 的最短转换序列。

  4. 字符串操作replace 函数展示了如何通过索引替换字符串中的字符。这涉及到字符串转字符数组,然后修改数组中的元素,最后将数组转回字符串。

  5. 循环和条件语句 :代码中使用了 for 循环来遍历单词的每个字符和字母表中的每个字符,以及 if 语句来检查条件。

  6. 函数定义和调用 :代码中定义了 ladderLengthgetNextWordsreplace 三个函数,分别用于解决不同的问题。函数的定义和调用展示了模块化编程的思想。

  7. 字符和字符数组 :代码中使用了字符变量 ch 来遍历字母表,并使用了字符数组 chars 来表示和操作字符串。

  8. 数据结构的选择 :代码中选择合适的数据结构(如 HashSetQueue)来优化算法的性能,特别是在单词查找和图遍历方面。

  9. 算法设计:代码实现了基于 BFS 的图遍历算法,这是一种常见的算法设计技巧,用于解决最短路径问题。

  10. 递归和迭代:虽然代码中没有直接使用递归,但 BFS 本身是一种递归思想的迭代实现,每次迭代都相当于展开递归的一层。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

相关推荐
辣香牛肉面7 分钟前
Linux下“/proc”目录的作用
java·linux·服务器
Ezekiel Mok21 分钟前
基于路径长度的样条插补算法(自动驾驶和路径跟踪控制适用)
算法·matlab·自动驾驶·路径跟踪·插补
skyshandianxia22 分钟前
java面试八股之MySQL怎么优化查询语句
java·mysql·面试
kussmcx29 分钟前
开始尝试从0写一个项目--后端(一)
java·spring·maven
yogima38 分钟前
在Spring Data JPA中使用@Query注解
java·数据库·spring
赫萝的红苹果43 分钟前
基于Redisson实现分布式锁
java·spring boot·分布式
mana飞侠44 分钟前
代码随想录算法训练营第59天:动态[1]
开发语言·数据结构·算法·动态规划
zengy51 小时前
代码随想录打卡第十三天
数据结构·c++·算法·leetcode
wang_book1 小时前
redis学习(003 数据结构和通用命令)
java·数据库·redis·学习
英雄汉孑1 小时前
图片压缩代码和实际操作页面
java