返回
LeetCode 433:最小基因变化的快速解法
前端
2023-12-04 21:09:06
在这篇博文中,我们将探讨 LeetCode 上一道颇具挑战性的问题——433. 最小基因变化。我们将使用双向 BFS(广度优先搜索)算法,这是一种高效且优雅的解决方法。让我们深入探讨这道题目的解题思路,了解为什么双向 BFS 是破解它的最佳途径。
双向 BFS 算法
双向 BFS 是一种算法,它从问题空间的两端开始搜索,同时向中间推进。当两端相遇时,它们就找到了连接起点和终点的最短路径。在 LeetCode 433 中,我们将从 start 和 end 两个单词开始搜索,向中间推进,直到两端的搜索相遇。
JavaScript 实现
以下是用 JavaScript 实现的双向 BFS 算法:
function minMutation(start, end, bank) {
// BFS 队列
const startQueue = [start];
const endQueue = [end];
// 已访问单词
const visited = new Set();
// 步数
let steps = 0;
while (startQueue.length && endQueue.length) {
// 处理较小队列
const queue = startQueue.length < endQueue.length ? startQueue : endQueue;
// 遍历队列中的单词
for (let i = 0; i < queue.length; i++) {
const word = queue[i];
if (visited.has(word)) continue;
visited.add(word);
// 如果单词相等,则找到最短路径
if (word === end) return steps;
// 生成变异单词
const nextWords = getNextWords(word);
// 将变异单词加入队列
for (const nextWord of nextWords) {
if (!visited.has(nextWord)) {
queue.push(nextWord);
}
}
}
// 从队列中移除已处理的单词
queue.splice(0, queue.length);
// 增加步数
steps++;
}
// 未找到最短路径
return -1;
}
function getNextWords(word) {
// 允许变异的位数
const allowedMutations = 1;
const nextWords = [];
// 遍历单词中的每个字符
for (let i = 0; i < word.length; i++) {
// 遍历所有可能的变异
for (const char of 'ACGT') {
// 跳过相同的字符
if (char === word[i]) continue;
// 生成变异单词
const newWord = word.substring(0, i) + char + word.substring(i + 1);
// 判断变异单词是否有效
if (bank.includes(newWord)) {
nextWords.push(newWord);
}
}
}
return nextWords;
}
优势
双向 BFS 在解决 LeetCode 433 时具有以下优势:
- 优化遍历次数: 每次推进都选取两端队列中较小者,这可以大大减少遍历的次数。
- 高效队列使用: BFS 需要使用队列,当一端队列元素的下一个变化单词存在于另一端的队列中时,即可认为两者相遇。使用 Map 判断单词是否存在于队列中,可以进一步优化搜索。
- 直观且易于理解: 双向 BFS 是一种直观且易于理解的算法,即使对于初学者也是如此。
总结
双向 BFS 是一种高效且优雅的方法,可以解决 LeetCode 433:最小基因变化问题。通过从起点和终点同时向中间推进,我们可以有效地找到最短路径。JavaScript 实现简洁且易于理解,使这一算法成为解决此类问题的理想选择。