【 每天学习一点算法 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)

相关推荐
yxc_inspire1 小时前
24年CCPC山东邀请赛补题
学习·算法
red_redemption1 小时前
自由学习记录(187)
学习
木子墨5161 小时前
工程算法实战 | 数据库ORDER BY的底层:内存排序 → 外部归并 → 索引优化
数据结构·数据库·python·sql·算法·动态规划
nashane1 小时前
HarmonyOS 6学习:Web组件与JavaScript交互的三大高频问题与终极解决方案
前端·学习·harmonyos
凉、介1 小时前
ARM GICv3 学习笔记(一)
arm开发·笔记·学习·嵌入式
YangYang9YangYan1 小时前
产品经理学习数据分析的价值与路径
学习·数据分析·产品经理
广州灵眸科技有限公司1 小时前
瑞芯微(EASY EAI)RV1126B 模型部署API说明
linux·开发语言·网络·人工智能·深度学习·算法·yolo
東隅已逝,桑榆非晚1 小时前
深⼊理解指针(5)
c语言·笔记·算法
星夜夏空991 小时前
STM32单片机学习(13) —— 串口通信协议
stm32·单片机·学习