LeetCode 面试经典 150_图的广度优先搜索_最小基因变化(93_433_C++_中等)
题目描述:
基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 'A'、'C'、'G' 和 'T' 之一。
假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。
- 例如,"AACCGGTT" --> "AACCGGTA" 就是一次基因变化。
另有一个基因库 bank 记录了所有有效的基因变化,只有基因库中的基因才是有效的基因序列。(变化后的基因必须位于基因库 bank 中)
给你两个基因序列 start 和 end ,以及一个基因库 bank ,请你找出并返回能够使 start 变化为 end 所需的最少变化次数。如果无法完成此基因变化,返回 -1 。
注意:起始基因序列 start 默认是有效的,但是它并不一定会出现在基因库中。
输入输出样例:
示例 1:
输入 :start = "AACCGGTT", end = "AACCGGTA", bank = ["AACCGGTA"]
输出:1
示例 2:
输入 :start = "AACCGGTT", end = "AAACGGTA", bank = ["AACCGGTA","AACCGCTA","AAACGGTA"]
输出:2
示例 3:
输入 :start = "AAAAACCC", end = "AACCCCCC", bank = ["AAAACCCC","AAACCCCC","AACCCCCC"]
输出:3
提示:
start.length== 8
end.length== 8
0 <= bank.length <= 10
bank[i].length == 8
start、end 和 bank[i] 仅由字符 ['A', 'C', 'G', 'T'] 组成
题解:
解题思路:
思路一(广度优先搜索(BFS)):
1、使用 广度优先搜索(BFS) 来解决这个问题的原因是,我们的目标是找到从 start 到 end 的 最短路径(即最少的基因变化次数)。
- 这个问题本质上是一个最短路径问题。我们从起始基因序列 start 出发,目标是通过一系列的基因变化最终得到目标基因序列 end,每次变化只能修改一个字符,而且变化后的基因序列必须出现在基因库 bank 中。
- 在图中,每个基因序列都可以看作是一个节点,两个基因序列之间的边表示它们只相差一个字符。
2、复杂度分析:
① 时间复杂度:O(N),需要遍历 N 个基因,每个基因产生最多 32 个变异,因此 BFS 的总遍历次数为 O(N * 32)。
② 空间复杂度:O(N),bank_set 的空间复杂度为 O(N),为了避免重复访问基因,代码维护了一个 unordered_set 为 O(N),队列的空间复杂度也是 O(N)。
代码实现
代码实现(思路一(广度优先搜索(BFS))):
cpp
class Solution {
public:
// 函数minMutation计算从startGene到endGene的最小变异步数
int minMutation(string startGene, string endGene, vector<string>& bank) {
// 将基因库bank转换为一个unordered_set方便快速查找
unordered_set<string> bank_set(bank.begin(), bank.end());
unordered_set<string> visited;
// 如果起始基因和目标基因相同,直接返回0
if(startGene == endGene) {
return 0;
}
// 如果目标基因不在基因库中,返回-1
if (!bank_set.count(endGene)) {
return -1;
}
// 初始化队列用于广度优先搜索(BFS),开始时队列只包含起始基因
queue<string> Q;
Q.push(startGene);
visited.insert(startGene); // 记录已访问的基因,防止重复访问
int step = 1; // 步数初始化为1(表示当前基因本身作为第一步)
// BFS循环,直到队列为空
while (!Q.empty()) {
int size = Q.size(); // 当前队列的大小,即当前层的基因数
for (int i = 0; i < size; i++) { // 遍历当前层的所有基因
string curStr = Q.front();
Q.pop();
// 对当前基因的每一位(8个位置)进行变异
for (int j = 0; j < 8; j++) {
// 遍历所有可能的字符('A', 'C', 'G', 'T')
for (auto &c : "ACGT") {
// 只有在当前基因和待变异的字符不相同的时候才进行变异
if(curStr[j] != c) {
string next = curStr; // 生成变异后的新基因
next[j] = c; // 将第j位基因变为字符c
// 如果新基因等于目标基因,返回当前的步数
if (next == endGene) {
return step;
}
// 如果新基因在基因库中并且未被访问过,则加入队列
if (!visited.count(next) && bank_set.count(next)) {
Q.push(next);
visited.insert(next); // 标记新基因为已访问
}
}
}
}
}
step++; // 增加步数,表示当前层的所有基因都已处理
}
// 如果没有找到目标基因,返回-1
return -1;
}
};
以思路一为例进行调试
cpp
#include<iostream>
#include<vector>
#include<unordered_set>
#include<queue>
using namespace std;
class Solution {
public:
// 函数minMutation计算从startGene到endGene的最小变异步数
int minMutation(string startGene, string endGene, vector<string>& bank) {
// 将基因库bank转换为一个unordered_set方便快速查找
unordered_set<string> bank_set(bank.begin(), bank.end());
unordered_set<string> visited;
// 如果起始基因和目标基因相同,直接返回0
if(startGene == endGene) {
return 0;
}
// 如果目标基因不在基因库中,返回-1
if (!bank_set.count(endGene)) {
return -1;
}
// 初始化队列用于广度优先搜索(BFS),开始时队列只包含起始基因
queue<string> Q;
Q.push(startGene);
visited.insert(startGene); // 记录已访问的基因,防止重复访问
int step = 1; // 步数初始化为1(表示当前基因本身作为第一步)
// BFS循环,直到队列为空
while (!Q.empty()) {
int size = Q.size(); // 当前队列的大小,即当前层的基因数
for (int i = 0; i < size; i++) { // 遍历当前层的所有基因
string curStr = Q.front();
Q.pop();
// 对当前基因的每一位(8个位置)进行变异
for (int j = 0; j < 8; j++) {
// 遍历所有可能的字符('A', 'C', 'G', 'T')
for (auto &c : "ACGT") {
// 只有在当前基因和待变异的字符不相同的时候才进行变异
if(curStr[j] != c) {
string next = curStr; // 生成变异后的新基因
next[j] = c; // 将第j位基因变为字符c
// 如果新基因等于目标基因,返回当前的步数
if (next == endGene) {
return step;
}
// 如果新基因在基因库中并且未被访问过,则加入队列
if (!visited.count(next) && bank_set.count(next)) {
Q.push(next);
visited.insert(next); // 标记新基因为已访问
}
}
}
}
}
step++; // 增加步数,表示当前层的所有基因都已处理
}
// 如果没有找到目标基因,返回-1
return -1;
}
};
int main(int argc, char const *argv[])
{
string startGene="AAAAACCC";
string endGene="AACCCCCC";
vector<string> bank={"AAAACCCC","AAACCCCC","AACCCCCC"};
Solution s;
int step= s.minMutation(startGene,endGene,bank);
cout<<"step="<<step<<endl;
return 0;
}
LeetCode 面试经典 150_LeetCode 面试经典 150_图的广度优先搜索_最小基因变化(93_433)原题链接
欢迎大家和我沟通交流(✿◠‿◠)