【 每天学习一点算法 2026/05/14】单词接龙

每天学习一点算法 2026/05/14

题目:单词接龙

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

每一对相邻的单词只差一个字母。

对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord 不需要在 wordList 中。

sk == endWord

给你两个单词 beginWord 和 endWord 和一个字典 wordList ,返回 从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0 。

这题很简单我们可以将所有的序列看成一个树结构,beginWord 为根节点,子节点是只差一个字母的单词,层序遍历就能找到最短的转换序列。

我们要思考一下如何判断两个单词只差一个字母,最简单的办法是写双重循环做判断,但是如果每个单词都要这样的对比话时间复杂就很高了,这里我们直接把当前单词的每一位,从 a 改到 z,生成所有可能的变体,再看变体是否在字典里。

例子:cat

  • 第 0 位:*at → aat, bat, cat, dat...zat
  • 第 1 位:c*t → cat, cbt, cct...czt
  • 第 2 位:ca* → caa, cab, cac...caz
typescript 复制代码
function ladderLength(beginWord: string, endWord: string, wordList: string[]): number {
  const set = new Set(wordList) // 使用 set 存储 wordList 便于查找相邻单词
  if (!set.has(endWord)) return 0 // 如果集合没有结尾单词直接返回 0
  const queue: Array<[string, number]> = [] // 队列用于层序遍历
  queue.push([beginWord, 1]) // 初始放入 beginWord 作为根节点
  // 进行层序遍历
  while (queue.length > 0) {
    const [word, level] = queue.shift() // 队首元素出队列
    // 寻找相邻单词
    for (let i = 0; i < word.length; i++) {
      // 替换当前节点单词的每以为字母,寻找匹配的相邻单词
      for (let code = 97; code <= 122; code++) {
        const newChar = String.fromCharCode(code) // 生成字母
        if (newChar === word[i]) continue // 原单词直接跳过
        const newWord = word.slice(0, i) + newChar + word.slice(i + 1) // 替换字母后的单词
        if (newWord === endWord) return level + 1 // 如果是结尾单词直接返回 level + 1
        // 如果是集合里的单词
        if (set.has(newWord)) {
          queue.push([newWord, level + 1]) // 作为下一层的节点push进队列
          set.delete(newWord) // 从集合中移除,避免重复使用
        }
      }
    }
  }
  return 0 // 遍历完毕还没有遇到结尾单词表示没有比配序列
};

这道题我们还可以利用双向广度搜索的方法来减少搜索事件。

双向广度搜索就是,从起点开始往外扩散 + 从终点开始往外扩散,两边在中间相遇时,就是最短路径。

核心思想

  1. 用两个集合:

    • beginSet:从 beginWord 扩散出来的层

    • endSet:从 endWord 扩散出来的层

  2. 关键点 :每次 选更小的集合扩展 ,减少搜索量

  3. 扩展时生成新单词,如果新单词出现在对面集合,说明相遇了

  4. 总步数 = 起点步数 + 终点步数 + 1

typescript 复制代码
function ladderLength(beginWord: string, endWord: string, wordList: string[]): number {
  const set = new Set(wordList) // 使用 set 存储 wordList 便于查找相邻单词
  if (!set.has(endWord)) return 0 // 如果集合没有结尾单词直接返回 0
  
  let beginSet = new Set([beginWord]) // 开始单词集合
  let endSet = new Set([endWord]) // 结束单词集合
  set.delete(endWord) // 在集合中删除结束单词
  let level = 1 // 初始化层数为 1
  
  // 进行双向广度搜索
  while (beginSet.size > 0) {
    const nextSet: Set<string> = new Set() // 用于存储本次扩展的单词
    // 将较小的集合作为扩展的目标
    if (beginSet.size > endSet.size) {
      [beginSet, endSet] = [endSet, beginSet]
    }
    // 对集合中的单词进行相邻单词扩展
    for (let word of beginSet) {
      for (let i = 0; i < word.length; i++) {
        for (let c = 97; c <= 122; c++) {
          const char = String.fromCharCode(c);
          if (char === word[i]) continue;
          const newWord = word.slice(0, i) + char + word.slice(i + 1); // 扩展单词
          if (endSet.has(newWord)) return level + 1 // 如果扩展单词出现在另一个集合中,两边相遇,找到最短路径
          if (set.has(newWord)) {
            set.delete(newWord) // 移除已使用单词
            nextSet.add(newWord) // 记录扩展单词
          }
        }
      }
    }
    beginSet = nextSet // 下一层集合赋值给扩展的集合(如果 set 为空,nextSet比为空,此时会跳出循环)
    level++
  }

  return 0
};

题目来源:力扣(LeetCode)

相关推荐
玖玥拾44 分钟前
LeetCode 125 验证回文串
算法·leetcode
Asize7 小时前
146. LRU 缓存
算法
Asize7 小时前
543. 二叉树的直径
算法
lemon_sjdk8 小时前
ObjectProperty
java·开发语言·算法
Zentceh8 小时前
AI-ISP在夜视机芯中的应用:从传统ISP到PixelClean全彩夜视的进化
人工智能·科技·算法·计算机视觉·车载系统·视频·智能硬件
xier_ran8 小时前
【infra之路】AWQ 详解:激活感知权重保护,让 W4A16 量化精度超越 GPTQ
线性代数·算法·机器学习·量化·infra
亦皓ai9 小时前
AI时代后端工程(二):AI把代码写得越来越快,我却越来越不敢让它直接开工了
人工智能·算法·机器学习·搜索引擎·transformer
小弥儿9 小时前
GitHub今日热榜 | 2026-08-26:AI 大脑与知识管理扎堆
人工智能·学习·开源·github
荷蒲9 小时前
【小白量化Qbuddy】利用AI学习中文Python
人工智能·python·学习
中科高级技工学校10 小时前
乌鲁木齐中医康复学习经历分享
大数据·学习