返回
LeetCode 剖析:DFS 和 BFS 的艺术——最小基因变化
前端
2023-11-03 19:19:22
导言
踏上 LeetCode 算法学习之旅,我们今天将直面一道困难级别的题目:433. 最小基因变化。这道题考察了算法中的两个核心概念:深度优先搜索 (DFS) 和广度优先搜索 (BFS)。让我们深入探讨解题思路,掌握这些算法的精妙之处。
题目
科学家正在研究一种由 n 个基因组成的疾病。一开始,患者只有 1 个突变基因。目标是 使用最少的基因突变将患者的基因组变为正常状态。
给定一个基因组的初始状态和目标状态,以及基因突变规则,求出将基因组从初始状态变为目标状态所需的最小基因突变数 。
算法选择
解决这道题的关键在于选择合适的算法。DFS 和 BFS 都是遍历图的经典算法,但它们的特性不同:
- DFS: 深度优先搜索沿着当前路径深入搜索,直到找到解或穷举所有可能。
- BFS: 广度优先搜索逐层探索所有路径,以确保找到最短路径。
DFS 的优势
对于这道题,DFS 的优势在于它的深度优先特性,可以快速找到从初始状态到目标状态的路径,即使路径较长。而且,DFS 不需要存储所有已访问过的状态,从而节省了内存空间。
解题思路
我们的解题思路如下:
- 将基因突变规则构建为邻接表,每个基因对应一个顶点,突变规则对应顶点之间的边。
- 从初始基因出发,使用 DFS 遍历邻接表,记录每一步的基因突变数。
- 当 DFS 到达目标基因时,记录此时的突变数。
- 在 DFS 过程中,维护一个哈希表记录已访问过的基因,避免重复访问。
BFS 的补充作用
虽然 DFS 对于这道题是主要算法,但 BFS 也可以在特定情况下提供帮助。如果基因突变规则的图非常密集,即每个基因与许多其他基因相连,则 BFS 的广度优先特性可以帮助我们更有效地探索所有路径。
代码实现
import collections
def min_mutation(start, end, bank):
"""
使用 DFS 算法求解最小基因突变数。
参数:
start (str): 初始基因。
end (str): 目标基因。
bank (list[str]): 突变规则。
返回:
int: 最小基因突变数。
"""
# 构建邻接表
graph = collections.defaultdict(list)
for gene in bank:
for i in range(len(gene)):
for c in 'ACGT':
if gene[i] != c:
graph[gene[:i] + c + gene[i+1:]].append(gene)
# 使用 DFS 遍历邻接表
visited = set()
queue = [(start, 0)]
while queue:
gene, mutation = queue.pop(0)
if gene == end:
return mutation
if gene not in visited:
visited.add(gene)
for neighbor in graph[gene]:
queue.append((neighbor, mutation + 1))
# 未找到路径
return -1
总结
通过对 DFS 和 BFS 特性的深入理解,我们可以针对不同场景选择合适的算法,从而高效解决 LeetCode 算法难题。433. 最小基因变化一题正是 DFS 和 BFS 优势的完美体现。掌握这些算法的精髓,我们将所向披靡,征服更多 LeetCode 挑战!