这个问题实际上是一个典型的最短路径问题,我们希望从数组的第一个元素(出发点)到最后一个元素(目的地)所需的最少步数s,在这里我们可以一步步向前或向后移动,或者直接跳到一个相同航空公司的机场✈。
解题思路😎
-
为什么使用 BFS嘞?🤔
- 广度优先搜索(BFS) 是解决最短路径问题的经典算法,因为 BFS 的特性是按层遍历,也就是说 BFS 遍历的第一条到达目的地的路径,必定是最短路径。
- 例如,从出发点开始,每次「飞行」一步,到达所有可能的新位置;然后再从这些位置继续「飞行」一步,直到到达目的地。这样我们就能找到从起点到终点的最少步数。
-
如何表示和存储机场与航空公司的关系?
- 使用
Map把数组中的每个航空公司值对应的所有机场位置(即数组中的索引)存起来。例如,机场数组[10, 12, 13, 12, 14]会形成这样的Map:{ 10: [0], 12: [1, 3], 13: [2], 14: [4] } - 这样,如果我们当前位于某个机场,比如
arr[1] = 12,我们就能快速找到所有航空公司为12的其他机场(即索引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);
}
}
代码逻辑分解👇
-
companyMap映射:记录每个航空公司所对应的机场,方便我们快速跳跃。 -
BFS 初始化:将出发点(
arr[0])加入队列queue,并设置起飞次数flights为 0。 -
BFS 遍历:对于每个层级(每次飞行):
- 检查相邻机场
i-1和i+1,如未访问则加入队列。 - 检查相同航空公司的机场列表
companyMap,对每个未访问的机场添加到队列。 - 一旦到达目的地
arr.length - 1,立即返回当前的起飞次数flights。
- 检查相邻机场
-
终止条件:如果到达目的地,则返回
flights。如果 BFS 结束时未找到路径,则返回-1(尽管题目中没有提到无法到达的情况)。
示例分析
对于 airports = [10, 12, 13, 12, 14]:
- 起点为 0,可以飞到 1 或跳跃到 3(同为
12)。 - 从 1 可以到 3,3 再飞到 4。
- 结果为 3 次飞行。
感恩字节跳动,感恩MarsCode AI,感恩青训营❤