力扣433题 最小基因变化
基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 ‘A’、‘C’、‘G’ 和 ‘T’ 之一。
假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。
例如,“AACCGGTT” --> “AACCGGTA” 就是一次基因变化。
另有一个基因库 bank 记录了所有有效的基因变化,只有基因库中的基因才是有效的基因序列。
给你两个基因序列 start 和 end ,以及一个基因库 bank ,请你找出并返回能够使 start 变化为 end 所需的最少变化次数。如果无法完成此基因变化,返回 -1 。
注意:起始基因序列 start 默认是有效的,但是它并不一定会出现在基因库中。
解法我觉得官方题解的BFS很不错,主要是借这道题目来练习写BFS(Breadth_First_Search,广度优先搜索)。
首先回顾数据结构这一节,BFS是在图的遍历引出来的,以广度优先的方式来遍历图的每一个节点。
首先有准备工作:图的存储,即使用邻接矩阵还是邻接表还是什么来存储图。接着开始广度优先遍历。
图的存储的意义:当我们遍历到一个顶点,通过邻接矩阵(等其他存储结构)可以知道从这个顶点出发,下一次搜索的顶点范围。
广度优先遍历,先从第一个顶点开始遍历,标记了该顶点后将该顶点放入队列中,接着从队列弹出这一层(第一个)的元素,依次遍历这些元素的相邻元素,并对这些相邻元素标记,放入队列。后续依次进行,直至全部遍历。其中需要添加一个数组,用来记录哪些顶点已经被遍历了,避免重复遍历。
详细的BFS算法可以参考《大话数据结构》第七章 图,第7.5 图的遍历中的广度优先遍历(P242)
PS:我的是2011年6月第1版,2020年3月第24次印刷
代码如下:(思路等注释在代码里面)
class Solution {
public int minMutation(String start, String end, String[] bank) {
//特殊情况,当end不在bank数组里面,直接返回-1
int n = bank.length;
boolean flag = true;
for (int i = 0; i < n; i++) {
if(end.equals(bank[i])){
flag = false;
}
}
if(flag) return -1;
//1.准备工作,存储与每个字符串相差一个字符的元素(类似与邻接矩阵)
List<Integer>[] obj = new List[n];//存储与第i个元素只相差一个字符的其他元素,类似邻接矩阵
for (int i = 0; i < n; i++) {
obj[i] = new ArrayList<>();
}
for (int i = 0; i < n; i++) {
for (int j = i+1; j < n; j++) {
if(judge(bank[i],bank[j])){
obj[i].add(j);
obj[j].add(i);
}
}
}
//2.BFS:一层一层地查询
Queue<Integer> queue = new ArrayDeque<>();//队列
boolean[] record = new boolean[n];//避免重复查询
int step = 1;
for (int i = 0; i < n; i++) {
if(judge(start,bank[i])){
//一步可以得到end情况
if(end.equals(bank[i])){
return step;
}else {
//走一步的情况,即这一层的元素入队
queue.offer(i);
record[i] = true;
}
}
}
step++;
while (!queue.isEmpty()){
//先将这一层的元素依次出队列,并判断每个元素的下一层是否满足条件
int sz = queue.size();
for(int i = 0;i<sz;i++){
//对每个元素进行判断
int temp = queue.poll();
for(int j:obj[temp]){
if(record[j]) continue;
if(end.equals(bank[j])){
return step;
}else {
record[j] = true;
queue.offer(j);
}
}
}
step++;
}
//包括了走一步无法到达bank里面的元素,返回-1
//以及已经遍历完了bank,还是无法到达两种情况
return -1;
}
//比较两个字符串是否只有一位不一样
private boolean judge(String s1, String s2) {
int cnt = 0;
for (int i = 0; i < s1.length(); i++) {
if(s1.charAt(i)!= s2.charAt(i)) cnt++;
}
if(cnt == 1) return true;
return false;
}
}