问题描述
小C和他的领导小F计划一次飞行,但由于严格的航空管制,他们的飞机仅能按特定的路线飞行:飞机只能飞往当前机场的相邻机场或相同航空公司管理的机场。为了减少起飞次数,小C需要制定最优的飞行路线。机场由一个数组airports
标识,其中:
- 数组每个元素代表一个独特的机场,元素的值代表不同的航空公司。
airports[0]
为起点,airports[airports.length - 1]
为终点。- 假设小C当前在机场
i
,那么i - 1
和i + 1
(如果存在)代表邻近机场,飞机可以直接前往。 - 如果在机场
i
,且存在airports[i] == airports[j]
,则机场i
和机场j
同属一家航空公司,可直接飞往。
求最小起飞次数。
测试样例
样例1:
输入:
airports = [10, 12, 13, 12, 14]
输出:3
样例2:
输入:
airports = [10, 11, 12, 13, 14]
输出:4
from collections import deque, defaultdict
def solution(airports):
n = len(airports)
if n == 1:
return 0 # 如果只有一个机场,则不需要起飞
# 构建图,graph[i]表示从机场i可以直接飞往的机场列表
graph = defaultdict(list)
for i in range(n):
if i > 0:
graph[i].append(i - 1) # 添加与前一个机场的连接
if i < n - 1:
graph[i].append(i + 1) # 添加与后一个机场的连接
for j in range(n):
if i != j and airports[i] == airports[j]:
graph[i].append(j) # 添加与同一航空公司其他机场的连接
# 使用广度优先搜索(BFS)来找到最短路径
queue = deque([(0, 0)]) # 队列中存储元组(当前机场, 起飞次数)
visited = set() # 记录已访问的机场
visited.add(0)
while queue:
current, flights = queue.popleft() # 取出队首元素
if current == n - 1:
return flights # 如果到达终点,返回起飞次数
for neighbor in graph[current]:
if neighbor not in visited:
visited.add(neighbor) # 标记邻近机场为已访问
queue.append((neighbor, flights + 1)) # 将邻近机场加入队列,并增加起飞次数
return -1 # 如果无法到达终点,返回-1
思路解析
题目要求的是从起点机场到终点机场的最小起飞次数,可以看作是一个最短路径问题。在这个问题中,每个机场可以看作图中的一个节点,机场之间的飞行路线可以看作边。给定起点(airports[0])和终点(airports[airports.length - 1]),需要找到从起点到终点的最短路径,而最短路径对应的就是最小起飞次数。
我们需要用合适的数据结构来表示机场之间的连通关系。由于机场之间的连通关系不是简单的线性关系,而是一种网状结构,所以使用图这种数据结构非常合适。在这个问题中,每个机场可以看作是图中的一个节点,如果两个机场可以直接飞往,就在它们之间连一条无权边。
在图中搜索从起点到终点的最短路径,这是一个经典的图搜索问题。对于无权图的最短路径问题,广度优先搜索(BFS)是最合适的算法。BFS能够保证第一次找到的路径就是最短路径,并且时间复杂度也较低。实现BFS时,我们需要使用队列来存储待访问的节点,并且使用一个集合来记录已访问过的节点,避免重复访问。在遍历过程中,如果遇到终点,就返回当前的路径长度(对应飞行次数)作为最小值;如果队列为空,说明无法从起点到达终点,返回-1。
BFS 是一种层次遍历的算法,它会先访问所有与起点相邻的节点,然后再访问这些节点的邻居。这样可以确保在找到终点机场时,所经过的路径是最短的,因为 BFS 是逐层扩展的。
使用 defaultdict 构建一个图 graph,键为机场编号,值为该机场可直接飞往的机场列表。对于每个机场 i,首先判断 i-1 和 i+1 是否在有效范围内,如果是,就将它们添加到 graph[i] 中,代表可以直接飞往相邻机场。然后遍历所有机场,如果机场 j 与机场 i 不同,但它们属于同一家航空公司(airports[i] == airports[j]),就将 j 添加到 graph[i] 中,代表可以直接飞往同一航空公司的其他机场
。初始化一个双端队列 queue,用于 BFS 遍历。队列中的元素是一个元组 (current, flights),分别表示当前机场编号和从起点到当前机场所需的起飞次数。由于起点是 0,所以先将 (0, 0) 加入队列。 另外,使用一个集合 visited 记录已访问过的机场,初始时将起点 0 加入。
进入 BFS 主循环,不断从队首取出元素。如果取出的 current 是终点 n-1,说明已经找到从起点到终点的路径,返回该路径所需的起飞次数 flights。 否则,遍历当前机场 current 可直接飞往的所有邻居 neighbor。如果该邻居未被访问过,就将它加入已访问集合 visited,并将 (neighbor, flights + 1) 加入队列,flights + 1 表示从起点到该邻居需要的起飞次数比到达当前机场多 1 次。