leetcode 127 单词接龙(图遍历、bfs)

110 阅读1分钟

单词接龙

字典 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 中的所有字符串 互不相同

解题

一句话题解

  • 无向图中(一次转换相当于一个边)两个顶点之间的最短路径的长度,可以通过广度优先遍历得到;

BFS 广度优先搜索 (2000 ms):

var ladderLength = function (beginWord, endWord, wordList) {
  if (wordList.indexOf(endWord) === -1) return 0
  var checkWord = function (begin, end) {
    let diff = 0
    for (let i = 0; i < begin.length; i++) {
      if (begin[i] !== end[i]) {
        diff += 1
        if (diff > 1) {
          return diff
        }
      }
    }
    return diff
  }
  let visited = {}
  wordList.forEach(item => {
    visited[item] = false
  })
  var bfs = function (arr, depth) {
    if (arr.indexOf(endWord) > -1) return depth
    let li = []
    arr.forEach(item => {
      for (let k in visited) {
        if (checkWord(k, item) === 1) {
          li.push(k)
          delete visited[k]
        }
      }
    })
    if (li.length === 0) {
      return 0
    }
    return bfs(li, depth + 1)
  }
  let res = bfs([beginWord], 1)
  return res
}

优化visted标记(400ms)

第一版耗时太长,做了一点优化,把 visited 从 Hash 改成 boolean 数组,通过 index 判断是否已访问。这样判断 visited 只需要 boolean 数组判断,节省了大量 Hash 操作。耗时下降为 400 ms。

var ladderLength = function (beginWord, endWord, wordList) {
  if (wordList.indexOf(endWord) === -1) return 0
  var checkWord = function (begin, end) {
    let diff = 0
    for (let i = 0; i < begin.length; i++) {
      if (begin[i] !== end[i]) {
        diff += 1
        if (diff > 1) {
          return diff
        }
      }
    }
    return diff
  }
  let visted = Array(wordList.length).fill(false)

  let bfs = function (list, depth) {
    let len = list.length
    let li = []
    while (len-- > 0) {
      let cur = list.shift()
      for (let i = 0; i < wordList.length; i++) {
        let s = wordList[i]
        if (visted[i] || checkWord(s, cur) !== 1) continue
        if (s === endWord) return depth + 2
        visted[i] = true
        li.push(s)
      }
    }
    if (li.length === 0) return 0
    return bfs(li, depth + 1)
  }
  return bfs([beginWord], 0)
}