class Solution {
public:
    vector<int> graph[100005];
    void dfs(int currNode,
            int parentNode,
            int &timeStamp,
            vector<bool> &vis,
            vector<int> &tin,
            vector<int> &low,
            vector<vector<int>> &critical)
    {
        vis[currNode] = true;
        tin[currNode] = low[currNode] = timeStamp++;
        //Travel to neighbours
        for(int i: graph[currNode]){
            if(i==parentNode)
                continue;
            if(!vis[i]){
                //Travel
                dfs(i, currNode, timeStamp, vis, tin, low, critical);
                low[currNode] = min(low[currNode], low[i]);
                //Once it's back we can see if can be reached or not
                if(low[i]>tin[currNode]){
                    critical.push_back({i, currNode});
                }
            }
            else
                low[currNode] = min(low[currNode], tin[i]);
        }
    }
    vector<vector<int>> criticalConnections(int n, vector<vector<int>>& connections) {
        vector<int> tin(n, INT_MAX);
        vector<int> low(n, INT_MAX);
        vector<vector<int>> critical;
        vector<bool> vis(n, false);
        for(int i = 0; i<n; i++)
            graph[i].clear();
        for(auto i: connections){
            graph[i[0]].push_back(i[1]);
            graph[i[1]].push_back(i[0]);
        }
        for(int i = 0; i<n; i++){
            if(!vis[i]){
                int timeStamp = 0;
                dfs(i, -1, timeStamp, vis, tin, low, critical);
            }
        }
        
        return critical;
    }
};