There are n servers numbered from 0 to n-1 connected by undirected server-to-server connections forming a network where connections[i] = [a, b] represents a connection between servers a and b. Any server can reach any other server directly or indirectly through the network.
A critical connection is a connection that, if removed, will make some server unable to reach some other server.
Return all critical connections in the network in any order.
Example 1:

Input: n = 4, connections = [[0,1],[1,2],[2,0],[1,3]]
Output: [[1,3]]
Explanation: [[3,1]] is also accepted.
Critical Connections in a Network - LeetCode
https://www.youtube.com/watch?v=ZeDNSeilf-Y&list=PLEJXowNB4kPzByLnnFYNSCoqtFz0VKLk5&index=28&ab_channel=TECHDOSE
https://www.youtube.com/watch?v=Rhxs4k6DyMM&list=PLEJXowNB4kPzByLnnFYNSCoqtFz0VKLk5&index=29&ab_channel=TECHDOSE
https://www.youtube.com/watch?v=HsBefuOqkd4&ab_channel=TECHDOSE
class Solution {
public:
vector<vector<int>> criticalConnections(int n, vector<vector<int>>& connections) {
vector<int> adj[n];
for(int i=0;i<connections.size();i++)
{
adj[connections[i][0]].push_back(connections[i][1]);
adj[connections[i][1]].push_back(connections[i][0]);
}
vector<vector<int>> bridges;
tarjans(n,bridges,adj);
return bridges;
}
void tarjans(int n, vector<vector<int>>&bridges,vector<int>adj[])
{
vector<int> dis(n,-1); //discovery times of vertices
vector<int> low(n,-1); //node with lowest discovery time accessible,
// its values changes when the dfs call returns
vector<int> parent(n,-1);//if node v comes after node u through an edge {u,v}
// then u is called the parent of v. here in the entire algorithm we do not consider the
// edges{v,u} i.e from child to parent
for(int i=0;i<n;i++)
{
if(dis[i]==-1)
dfs(i,n,bridges,adj,dis,low,parent);
}
}
void dfs(int u,int n,vector<vector<int>> &bridges,vector<int>adj[],vector<int> &dis,vector<int> &low,vector<int> &parent)
{
static int time = 0;
dis[u] = time; //value of discovery time of u, remains constant throughout the course of algorithm
low[u] = time; //initial value of low[u], may change during the course of algorithm
time++;
for(int v:adj[u])
{
if(dis[v]==-1)
{
parent[v] = u;
dfs(v,n,bridges,adj,dis,low,parent);
//when the dfs call for vertex v in the edge{u,v} ends update the low[u]
low[u] = min(low[u],low[v]); //if edge {u,v} is not a back edge
if(dis[u]<low[v])
bridges.push_back(vector<int>({u,v}));
}
else if(v!=parent[u])//Ifa new child v is visited
// for which dis[v]!=-1 and v is not the parent of u
// then the edge {u,v} is a backedge, which can never be a cut edge
low[u] = min(low[u],dis[v]);//if edge {u,v} is a back edge
}
}
};