啊D的最佳飞行路线探索| 豆包MarsCode AI 刷题

171 阅读4分钟

这个问题实际上是一个典型的最短路径问题,我们希望从数组的第一个元素(出发点)到最后一个元素(目的地)所需的最少步数s,在这里我们可以一步步向前或向后移动,或者直接跳到一个相同航空公司的机场✈

解题思路😎

  1. 为什么使用 BFS嘞?🤔

    • 广度优先搜索(BFS) 是解决最短路径问题的经典算法,因为 BFS 的特性是按层遍历,也就是说 BFS 遍历的第一条到达目的地的路径,必定是最短路径。
    • 例如,从出发点开始,每次「飞行」一步,到达所有可能的新位置;然后再从这些位置继续「飞行」一步,直到到达目的地。这样我们就能找到从起点到终点的最少步数。
  2. 如何表示和存储机场与航空公司的关系?

    • 使用 Map 把数组中的每个航空公司值对应的所有机场位置(即数组中的索引)存起来。例如,机场数组 [10, 12, 13, 12, 14] 会形成这样的 Map
      {
          10: [0],
          12: [1, 3],
          13: [2],
          14: [4]
      }
      
    • 这样,如果我们当前位于某个机场,比如 arr[1] = 12,我们就能快速找到所有航空公司为 12 的其他机场(即索引 3),从而可以直接「跳跃」过去。
  3. BFS 搜索的步骤

    • 从出发点(索引 0)开始,逐步尝试到达目的地(索引 arr.length - 1)。
    • 对于每个机场,我们可以有以下三种「飞行」方式:
      • ✈向前飞到相邻的下一个机场(即索引 i+1),如果它未访问过。
      • 🛩向后飞到相邻的上一个机场(即索引 i-1),如果它未访问过。
      • 🛫跳跃飞行到同一航空公司的其他机场(即 Map 中相同航空公司对应的其他索引),如果这些机场未访问过。
    • 每次飞行后将新到达的机场加入到 BFS 队列,继续搜索,直到到达目的地。

代码实现详解

我们来看代码中每一步的具体操作。👨‍💻

import java.util.*;

public class Main {
    public static int solution(int[] airports) {
        int n = airports.length;
        if (n == 1) return 0; // 特殊情况:如果出发点就是目的地

        // 步骤1:建立航空公司 -> 机场索引的映射
        Map<Integer, List<Integer>> companyMap = new HashMap<>();
        for (int i = 0; i < n; i++) {
            companyMap.computeIfAbsent(airports[i], k -> new ArrayList<>()).add(i);
        }

        // 步骤2:BFS 初始化
        Queue<Integer> queue = new LinkedList<>(); // 队列用于层次遍历
        boolean[] visited = new boolean[n];        // 标记是否访问过
        queue.offer(0);                            // 从出发点开始
        visited[0] = true;
        
        int flights = 0; // 记录飞行次数

        // 步骤3:BFS 遍历
        while (!queue.isEmpty()) {
            int size = queue.size();
            flights++; // 每层遍历意味着增加一次飞行次数

            for (int i = 0; i < size; i++) {
                int curr = queue.poll();

                // 检查前后相邻机场
                if (curr - 1 >= 0 && !visited[curr - 1]) {
                    if (curr - 1 == n - 1) return flights; // 到达目的地
                    queue.offer(curr - 1);
                    visited[curr - 1] = true;
                }
                if (curr + 1 < n && !visited[curr + 1]) {
                    if (curr + 1 == n - 1) return flights; // 到达目的地
                    queue.offer(curr + 1);
                    visited[curr + 1] = true;
                }

                // 检查同航空公司的其他机场
                if (companyMap.containsKey(airports[curr])) {
                    for (int next : companyMap.get(airports[curr])) {
                        if (!visited[next]) {
                            if (next == n - 1) return flights; // 到达目的地
                            queue.offer(next);
                            visited[next] = true;
                        }
                    }
                    // 清空这个航空公司下的机场列表,避免重复跳跃
                    companyMap.remove(airports[curr]);
                }
            }
        }

        return -1; // 如果无法到达目的地
    }

    public static void main(String[] args) {
        int[] airports1 = {10, 12, 13, 12, 14};
        int[] airports2 = {10, 11, 12, 13, 14};

        System.out.println(solution(airports1) == 3);
        System.out.println(solution(airports2) == 4);
    }
}

代码逻辑分解👇

  1. companyMap 映射:记录每个航空公司所对应的机场,方便我们快速跳跃。

  2. BFS 初始化:将出发点(arr[0])加入队列 queue,并设置起飞次数 flights 为 0。

  3. BFS 遍历:对于每个层级(每次飞行):

    • 检查相邻机场 i-1i+1,如未访问则加入队列。
    • 检查相同航空公司的机场列表 companyMap,对每个未访问的机场添加到队列。
    • 一旦到达目的地 arr.length - 1,立即返回当前的起飞次数 flights
  4. 终止条件:如果到达目的地,则返回 flights。如果 BFS 结束时未找到路径,则返回 -1(尽管题目中没有提到无法到达的情况)。

示例分析

对于 airports = [10, 12, 13, 12, 14]

  1. 起点为 0,可以飞到 1 或跳跃到 3(同为 12)。
  2. 从 1 可以到 3,3 再飞到 4。
  3. 结果为 3 次飞行。

感恩字节跳动,感恩MarsCode AI,感恩青训营❤