青训营X豆包MarsCode:技术训练营 | 阿D的最佳飞行路线探索

2 阅读5分钟

问题描述

小C和他的领导小F计划一次飞行,但由于严格的航空管制,他们的飞机仅能按特定的路线飞行:飞机只能飞往当前机场的相邻机场或相同航空公司管理的机场。为了减少起飞次数,小C需要制定最优的飞行路线。机场由一个数组airports标识,其中:

  • 数组每个元素代表一个独特的机场,元素的值代表不同的航空公司。
  • airports[0]为起点,airports[airports.length - 1]为终点。
  • 假设小C当前在机场i,那么i - 1i + 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 次。