1. 程式人生 > 其它 >[LeetCode]2045. BFS 求第二短的路徑

[LeetCode]2045. BFS 求第二短的路徑

本文介紹了一種使用BFS求解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> firstvector<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為邊個數