问题描述
小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
样例3:
输入:airports = [7, 7, 7, 8, 9, 7]
输出:
1
解题思路:
一开始我的思路是,记录每个机场最先出现的distance,然后在顺序的上一个机场和同名机场中找取最小值,作为当前节点的distance。
int solution(std::vector<int> airports) {
// Please write your code here
unordered_map<int,int> m;
vector<int> dis(airports.size(),-1);
dis[0] = 0;
m[airports[0]] = 0;
for(int i=1;i<airports.size();++i){
auto it = m.find(airports[i]);
if(it!=m.end()){
dis[i] = min(dis[i-1],m[airports[i]])+1;
}
else {
dis[i] = dis[i-1]+1;
m[airports[i]] = dis[i];
}
}
return dis[airports.size()-1];
}
但是当我进行测试的时候,却无论如何也通不过,我就感觉很奇怪,最后发现题目中给的条件是,可以飞往当前机场的相邻机场或相同航空公司管理的机场。而我在上面的代码中实际上只考虑了前一个机场,这就是出现问题的关键。但是如果我采用单次的顺序遍历,那么注定后面的机场是未操作的。所以采取BFS的算法来解决这道题可能是一个比较好的选择。
广度优先搜索(BFS)是一种用于遍历图或树的算法,采用层次遍历的方式,从一个指定的起始节点开始,利用队列结构管理待访问的节点。BFS 每次从队列中取出一个节点,访问该节点并将所有未被访问的相邻节点添加到队列中,确保按照距离起始节点的层次顺序进行访问。通过这种方式,BFS 可以有效地找到无权图中两个节点之间的最短路径,因为它总是优先访问离起始节点最近的节点。同时,BFS 需要使用一个布尔数组或集合来标记已访问的节点,以防止重复访问和无限循环。由于其逐层遍历的特点,BFS 在解决社交网络分析、网络流量、状态空间搜索等问题中具有广泛应用。尽管 BFS 的空间复杂度通常为 O(V)(其中 V 是图中的节点数),但它在许多实际场景中仍然是一种高效且有效的搜索算法。
采用BFS算法,按照一层一层的顺序进行搜索,并且在访问了某一个节点之后,将该节点标记为已访问,从而进行减枝,这是因为我们是按照飞行次数进行BFS的,那么如果某个节点已经被访问了,那么之后访问该节点的飞行次数一定是大于先前的飞行次数的,那么就可以停止继续从该节点进行搜索,从而减少运算量。
另一方面,我们需要找到每个节点的可搜寻的子节点,在这道题中,子节点包括前一个机场、后一个机场以及所有的同名机场。在BFS的过程中,对于每个节点,找到其所有可访问的子节点,作为下一层搜寻的对象。
使用BFS算法的比较适配的数据结构是队列,在c++中可以是queue。如果是DFS,那么可以采用栈或者直接使用递归算法,也就是系统栈来进行搜索。
下面是我的具体的实现代码:
int solution(std::vector<int> airports) {
int n = airports.size();
if (n == 1) return 0; // 只有一个机场,起飞次数为0
// 记录每个航空公司对应的机场索引
std::unordered_map<int, std::vector<int>> airlineMap;
for (int i = 0; i < n; ++i) {
airlineMap[airports[i]].push_back(i);
}
// BFS 初始化
std::queue<int> q;
std::vector<bool> visited(n, false);
q.push(0); // 从起始点出发
visited[0] = true;
int steps = 0;
while (!q.empty()) {
int size = q.size();
for (int i = 0; i < size; ++i) {
int current = q.front();
q.pop();
// 如果到达目的地
if (current == n - 1) {
return steps;
}
// 访问相邻的机场
if (current - 1 >= 0 && !visited[current - 1]) {
visited[current - 1] = true;
q.push(current - 1);
}
if (current + 1 < n && !visited[current + 1]) {
visited[current + 1] = true;
q.push(current + 1);
}
// 访问相同航空公司的机场
int airline = airports[current];
for (int idx : airlineMap[airline]) {
if (!visited[idx]) {
visited[idx] = true;
q.push(idx);
}
}
// 在访问过后清空同一航空公司的机场,避免重复访问
airlineMap[airline].clear();
}
steps++;
}
return -1; // 如果没有办法到达目的地(理论上不会发生)
}