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

2024-06-14 12:44

本文主要是介绍LeetCode题练习与总结:单词接龙Ⅱ--126,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、题目描述

按字典 wordList 完成从单词 beginWord 到单词 endWord 转化,一个表示此过程的 转换序列 是形式上像 beginWord -> s1 -> s2 -> ... -> sk 这样的单词序列,并满足:

  • 每对相邻的单词之间仅有单个字母不同。
  • 转换过程中的每个单词 si1 <= i <= k)必须是字典 wordList 中的单词。注意,beginWord 不必是字典 wordList 中的单词。
  • sk == endWord

给你两个单词 beginWord 和 endWord ,以及一个字典 wordList 。请你找出并返回所有从 beginWord 到 endWord 的 最短转换序列 ,如果不存在这样的转换序列,返回一个空列表。每个序列都应该以单词列表 [beginWord, s1, s2, ..., sk] 的形式返回。

示例 1:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出:[["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]
解释:存在 2 种最短的转换序列:
"hit" -> "hot" -> "dot" -> "dog" -> "cog"
"hit" -> "hot" -> "lot" -> "log" -> "cog"

示例 2:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
输出:[]
解释:endWord "cog" 不在字典 wordList 中,所以不存在符合要求的转换序列。

提示:

  • 1 <= beginWord.length <= 5
  • endWord.length == beginWord.length
  • 1 <= wordList.length <= 500
  • wordList[i].length == beginWord.length
  • beginWordendWord 和 wordList[i] 由小写英文字母组成
  • beginWord != endWord
  • wordList 中的所有单词 互不相同

二、解题思路

  1. 初始化:将beginWord添加到队列中,并设置其距离为0。初始化一个图graph来存储每个单词的相邻单词列表,和一个距离表distance来存储每个单词到beginWord的距离。

  2. 广度优先搜索:当队列不为空时,进行BFS。每次从队列中取出一个单词,并尝试将其每个位置的字母替换为’a’到’z’中的其他字母,生成新的单词。如果这个新单词在字典wordSet中且没有被访问过,或者已经被访问过但距离更短,则将其添加到队列中,并更新其距离和父节点。

  3. 找到最短路径:如果在BFS过程中找到了endWord,则标记找到并停止BFS。

  4. 回溯找到所有路径:如果找到了endWord,则从endWord开始回溯,通过父节点信息找到所有从beginWordendWord的最短路径,并将这些路径添加到结果列表中。

  5. 返回结果:如果找到了至少一条路径,则返回这些路径;如果没有找到,则返回空列表。

这个算法确保了只有最短路径会被考虑,因为它在找到endWord后停止了BFS,并且在回溯时只考虑了最短路径上的单词。这样可以避免生成和存储非最短路径,从而节省内存并提高效率。

三、具体代码

import java.util.*;public class Solution {public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {Set<String> wordSet = new HashSet<>(wordList);if (!wordSet.contains(endWord)) return new ArrayList<>();// 使用双向BFSMap<String, List<String>> graph = new HashMap<>();Map<String, Integer> distance = new HashMap<>();Queue<String> queue = new LinkedList<>();queue.add(beginWord);distance.put(beginWord, 0);boolean found = false;while (!queue.isEmpty()) {int level = distance.get(queue.peek());int size = queue.size();for (int i = 0; i < size; i++) {String current = queue.poll();char[] chars = current.toCharArray();for (int j = 0; j < chars.length; j++) {char original = chars[j];for (char c = 'a'; c <= 'z'; c++) {if (c == original) continue;chars[j] = c;String newWord = new String(chars);if (distance.containsKey(newWord) && distance.get(newWord) == level + 1) {graph.computeIfAbsent(newWord, k -> new ArrayList<>()).add(current);} else if (!distance.containsKey(newWord) && wordSet.contains(newWord)) {distance.put(newWord, level + 1);queue.add(newWord);graph.computeIfAbsent(newWord, k -> new ArrayList<>()).add(current);}}chars[j] = original;}}if (distance.containsKey(endWord)) {found = true;break;}}// 如果没有找到endWord,返回空列表if (!found) return new ArrayList<>();// 回溯找到所有路径List<List<String>> result = new ArrayList<>();List<String> path = new ArrayList<>();path.add(endWord);backtrack(graph, endWord, beginWord, path, result);return result;}private void backtrack(Map<String, List<String>> graph, String current, String beginWord, List<String> path, List<List<String>> result) {if (current.equals(beginWord)) {Collections.reverse(path);result.add(new ArrayList<>(path));Collections.reverse(path);return;}if (!graph.containsKey(current)) return;for (String next : graph.get(current)) {path.add(next);backtrack(graph, next, beginWord, path, result);path.remove(path.size() - 1);}}
}

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

1. 时间复杂度

  • 构建图的时间复杂度:对于每个单词,我们需要检查其所有可能的邻居(即通过改变每个位置的字母),共有wordList.size()个单词,每个单词有wordLength个字母,因此时间复杂度为O(wordList.size() * wordLength * 26),因为字母表有26个字母。
  • BFS的时间复杂度:在最坏情况下,我们需要访问图中的每个节点,并且对于每个节点,我们可能需要检查其所有邻居。因此,时间复杂度为O(V + E),其中V是节点数,E是边数。在这个问题中,V最大为wordList.size()E最大为wordList.size() * wordLength * 26(每个单词都有可能与其他所有单词相连)。
  • 回溯的时间复杂度:在最坏情况下,我们需要回溯所有从beginWordendWord的路径。每条路径的长度为pathLength,因此时间复杂度为O(pathLength * numPaths),其中numPaths是最短路径的数量。
  • 综合以上,总的时间复杂度O(wordList.size() * wordLength * 26 + wordList.size() * wordLength * 26 + pathLength * numPaths)

2. 空间复杂度

  • 图的空间复杂度:我们需要存储每个单词的邻居列表,因此空间复杂度为O(wordList.size() * wordLength * 26)
  • BFS队列的空间复杂度:在队列中,我们最多可能存储所有的单词,因此空间复杂度为O(wordList.size())
  • 距离表和路径列表的空间复杂度:这两个数据结构的空间复杂度也是O(wordList.size())
  • 回溯过程中的路径列表空间复杂度:在回溯过程中,我们为每条路径维护一个列表,因此空间复杂度为O(pathLength * numPaths)

在实际应用中,由于每个单词的邻居数量通常远小于26个,因此实际的时间和空间复杂度可能会低于上述分析的上限。此外,由于BFS在找到最短路径后会停止,回溯过程中只考虑最短路径,这也会降低实际的时间和空间复杂度。

五、总结知识点

  1. 广度优先搜索(BFS):这是一种图遍历算法,用于从起点开始搜索最近的节点。在这个问题中,我们使用BFS来找到从beginWordendWord的最短转换序列。

  2. 双向BFS:这是一种优化的BFS,从起点和终点同时进行搜索,直到两者相遇。这样可以减少搜索空间,提高效率。

  3. 图的表示:我们使用一个哈希表graph来表示图,其中键是单词,值是与其相邻的单词列表。

  4. 哈希集合Set:用于存储wordList中的单词,以便快速判断一个单词是否在字典中。

  5. 队列Queue:用于在BFS中存储待访问的节点。

  6. 回溯算法:这是一种递归算法,用于从终点回溯到起点,找到所有可能的转换序列。

  7. 列表List和数组ArrayList:用于存储路径和单词的邻居。

  8. 字符数组和字符串操作:在代码中,我们使用了字符数组来表示单词,并通过改变字符数组中的字符来生成新的单词。

  9. Java中的char类型和字符遍历:代码中使用了char类型来表示字母,并通过循环遍历az来尝试所有可能的字符替换。

  10. Java中的MapSet的操作:代码中使用了MapSetcontainsKeyputcomputeIfAbsent等方法来进行图的操作和单词的查找。

  11. Java中的Collections:用于操作集合,如reverse方法用于反转列表。

  12. 递归函数backtrack函数是一个递归函数,用于回溯找到所有可能的转换序列。

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

这篇关于LeetCode题练习与总结:单词接龙Ⅱ--126的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1060441

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

二分最大匹配总结

HDU 2444  黑白染色 ,二分图判定 const int maxn = 208 ;vector<int> g[maxn] ;int n ;bool vis[maxn] ;int match[maxn] ;;int color[maxn] ;int setcolor(int u , int c){color[u] = c ;for(vector<int>::iter

整数Hash散列总结

方法:    step1  :线性探测  step2 散列   当 h(k)位置已经存储有元素的时候,依次探查(h(k)+i) mod S, i=1,2,3…,直到找到空的存储单元为止。其中,S为 数组长度。 HDU 1496   a*x1^2+b*x2^2+c*x3^2+d*x4^2=0 。 x在 [-100,100] 解的个数  const int MaxN = 3000

状态dp总结

zoj 3631  N 个数中选若干数和(只能选一次)<=M 的最大值 const int Max_N = 38 ;int a[1<<16] , b[1<<16] , x[Max_N] , e[Max_N] ;void GetNum(int g[] , int n , int s[] , int &m){ int i , j , t ;m = 0 ;for(i = 0 ;

RabbitMQ练习(AMQP 0-9-1 Overview)

1、What is AMQP 0-9-1 AMQP 0-9-1(高级消息队列协议)是一种网络协议,它允许遵从该协议的客户端(Publisher或者Consumer)应用程序与遵从该协议的消息中间件代理(Broker,如RabbitMQ)进行通信。 AMQP 0-9-1模型的核心概念包括消息发布者(producers/publisher)、消息(messages)、交换机(exchanges)、

leetcode-24Swap Nodes in Pairs

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode swapPairs(L

leetcode-23Merge k Sorted Lists

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode mergeKLists