[LeetCode] 2045. 到达目的地的第二短时间
一、摘要
本文介绍了一种使用BFS求无向图中第二短的距离的算法,本文算法参考自Python3 BFS找出第二短路径。通过使用BFS求出题目中给出的路径图(无向联通图)从节点1到节点n的第二短的路径,再根据路径长度计算出需要耗费的时间。其实在LeetCode官网本题目的题解栏目下已经有很多优秀的题解了,本文除了是使用C++与其他题解有些许不同外无任何优势。还是建议读者阅读其他高赞题解。
二、题解
由于给给节点上红灯、绿灯的亮暗是一致的,因此将各节点间的边长视作1,只要求出从节点0
到节点n-1
经过的路径长度即可得到总的耗费时间。
因此本题就变成了求节点0
到节点n-1
的第二短的长度。
需要注意的是,我们要求的是绝对的第二短的路径,如果有两条最短的不同路径长度都为x,那么我还需要继续求得一个大于x的第二短路径。既然是等边长图中,求最短路径我们可以使用BFS。
使用一个队列queue<pair<int, int>>
记录所搜到的节点id
和从节点0到该节点id的耗时
。
为了求第二短的距离因此我们是用两个数组vector<int> first
和vector<int> second
分别记录节点0
到节点id
的第一、二短耗时(初始时都设为最大值,在C++中即为INT_MAX)。
在使用队列进行bfs算法时:
- 如果下一个可能要入队列的
节点n_id
需要的耗时n_time
小于first[n_id]
,那么说明节点0
到节点n_id
的最短耗时为n_time,需要入队pair<int,int>(n_id, n_time)``。 - 如果下一个可能要入队列的
节点n_id
需要的耗时n_time
等于first[n_id]
,那么说明存在重复的 从节点0
到节点n_id
的耗时相同、但路径不同 的最短耗时,此时不需要入队; - 如果下一个可能要入队列的
节点n_id
需要的耗时n_time
大于first[n_id]
,且小于second[n_id]
,那么说明节点0
到节点n_id
的第二短耗时为n_time,需要入队pair<int, int>(n_id, n_time)
。 - 如果下一个可能要入队列的
节点n_id
需要的耗时n_time
大于等于second[n_id]
,那么说明存在重复的 从节点0
到节点n_id
的耗时相同、但路径不同 的第二短耗时、或者这是第三短的耗时,此时不需要入队;
代码如下:
class Solution {
public:
// 图的节点
struct Node{
int idx; // 节点id
vector<int> neighbor; // 相邻的节点id
Node(int i):idx(i){}
};
// 根据此时的时间now计算经过一条边,到达下一个节点的时间
int getNextTime(int now, int time, int change){
if((now/change)%2==0){
now += time;
}else{
now += (change - now%change)+time;
}
return now;
}
int secondMinimum(int n, vector<vector<int>>& edges, int time, int change) {
nodes_.resize(n);
for(int i=0; i<n; i++){
nodes_[i] = new Node(i);
}
// 建图
for(int i=0; i<edges.size(); i++){
int a = edges[i][0] - 1;
int b = edges[i][1] - 1;
nodes_[a]->neighbor.push_back(b);
nodes_[b]->neighbor.push_back(a);
}
queue<pair<int, int>> que;
vector<int> first(n, INT_MAX); // first记录到达节点i最短的耗时
vector<int> second(n, INT_MAX); // second记录到达节点i第二短的耗时
que.push(pair<int, int>(0,0)); // 将(0,0)入队,表示达到节点0的耗时为0
while(que.empty()==false){
int id = que.front().first;
int now = que.front().second;
que.pop();
for(int i=0; i<nodes_[id]->neighbor.size(); i++){
int n_id = nodes_[id]->neighbor[i];
int n_time = getNextTime(now, time, change);
if(n_id==n-1){ // 如果是第n个节点
if(first[n_id]==INT_MAX){ // 第一次到达节点n-1,更新first,且pair<int,int>(n_id,n_time)入队
first[n_id] = n_time;
que.push(pair<int, int>(n_id, n_time));
}else if(n_time>first[n_id] && second[n_id]==INT_MAX){ // 到达节点n-1的第二短距离,返回答案
second[n_id] = n_time;
return n_time;
}
}else{
if(first[n_id]==INT_MAX){ // 第一次到达节点n_id,更新first,且pair<int,int>(n_id,n_time)入队
first[n_id] = n_time;
que.push(pair<int, int>(n_id, n_time));
}else if(n_time>first[n_id] && second[n_id]==INT_MAX){ // 到达节点n-1的第二短的距离,更新second,且pair<int,int>(n_id,n_time)入队
second[n_id] = n_time;
que.push(pair<int, int>(n_id, n_time));
}
}
}
}
return 0;
}
vector<Node*> nodes_;
};
复杂度分析:
- 时间复杂度:O(n+m)
- 空间复杂度:O(n+m)
其中n为节点个数,m为边个数