I have seen ways to detect a cycle in a graph, but I still have not managed to find a way to detect a "bridge-like" cycle. So let's say we have found a cycle in a connected (and undirected) graph. How can we determine whether removing this cycle will disconnect the graph or not? By removing the cycle, I mean removing the edges in the cycle (so the vertices are unaffected).
One way to do it is clearly to count the number of components before and after the removal. I'm just curious to know if there's a better way.
If there happens to be an established algorithm for that, could anyone please point me to a related work/paper/publication?
Here's the naive algorithm, complexity wise I don't think there's a more efficient way of doing the check.
Start with your list of edges (cycleEdges)
Get the set of vertices within cycleEdges (cycleVertices)
If a vertex in cycleVertices only contains edges that are part of cycleEdges return FALSE
For Each vetex In cycleVertices
Recursively follow vertex's edges that are not in cycleEdges (avoid already visited vertices)
If a vertex is reached that is not in cycleVertices add it to te set outsideVertices (stop recursively searching this path)
If only vertices that are in cycleVertices have been reached Return FALSE
If outsideVertices contains 1 element Return TRUE
Choose a vertex in outsideVertices and remove it from outsideVertices
Recursively follow that vertex's edges that are not in cycleEdges (avoid already visited vertices) (favor choosing edges that contain a vertex in outsideVertices to improve running time for large graphs)
If a vertex is reached that is in outsideVertices remove it from outsideVertices
If outsideVertices is empty Return TRUE
Return FALSE
You can do it for E+V.
You can get all bridges in your graph for E+V by dfs + dynamic programming.
http://www.geeksforgeeks.org/bridge-in-a-graph
Save them (just make boolean[E], and make true.
Then you can say for O(1) the edge is bridge or not.
You can just take all edges from your cycle and verify that it is bridge.
Vish's mentions articulation points which is definitely in the right direction. More can be said though. Articulation points can be found via a modified DFS algorithm that looks something like this:
Execute DFS, assigning each number with its DFS number (e.g. the number of nodes visited before it). When you encounter a vertex that has already been discovered compare its DFS number to the current vertex and you can store a LOW number associated with this vertex (e.g. the lowest DFS number that this node has "seen"). As you recurse back to the start vertex, you can compare the parent vertex with the child's LOW number. As you're recursing back, if a parent vertex ever sees a child's low number that is greater than or equal to its own DFS number, then that parent vertex is an articulation point.
I'm using "child" and "parent" here as descriptors a lot because in the DFS tree we have to consider a special case for the root. If it ever sees a child's low number that is greater than or equal to its own DFS number and it has two children in the tree, then the first vertex is an articulation.
Here's a useful art. point image
Another concept to be familiar with, especially for undirected graphs, is biconnected components, aka any subgraph whose vertices are in a cycle with all other vertices.
Here's a useful colored image with biconnected components
You can prove that any two biconnected components can only share one vertex max; two "shared" vertices would mean that the two are in a cycle, as well as with all the other vertices in the components so the two components are actually one large component. As you can see in the graph, any vertex shared by two components (has more than one color) is an articulation point. Removing the cycle that contains any articulation point will thus disconnect the graph.
Well, as in a cycle from any vertex x can be reached any other vertex y and vice-verse, then it's a strongly connected component, so we can contract a cycle into a single vertex that represents the cycle. The operation can be performed for O(n+m) using DFS. Now, we can apply DFS again in order to check whether the contracted cycles are articulation vertices, if they are, then removing them will disconnect a graph, else not. Total time is 2(n+m) = O(n+m)
Related
I have a graph and a starting node. I want to find how many nodes become isolated when I remove each node in the graph, for all nodes, using DFS.
For example, if I start on a fixed node 1, and remove node 2, how many isolated nodes I will have? and if I remove node 3?
I know I can just do DFS for all nodes(removing a different node each time), but doing so I will have to navigate the graph one time for each node, I want to solve it with just one run.
I have been told it has O(|V|*||A|), being |V|=number of edges, and |A|= number of nodes.
I've been playing with prenum and postnums, but with no success.
Let N be the number of vertices and M be the number of edges. If you just want a O(NM) solution as you stated, you don't need to go any further than running a DFS for each vertex.
The complexity for each DFS is O(N+M) so the total complexity will be of O(N(N+M)) = O(N²+NM). Usually we have more edges than vertices, so NM grows much faster than N² and we can say that the complexity is of O(NM). Keep in mind, though, that if you physically delete the current vertex at each step your implementation will have a much worse complexity, because physically deleting a vertex means removing entries from a lot of adjacency lists, which is costly no matter how you represent the graph. There is an implementation trick to speed up the process: instead of physically deleting the current vertex before each DFS, just mark the vertex as deleted, and when you are going through the adjacency lists during the DFS just ignore the marked vertex.
However, i feel that you can solve this problem in O(N+M) using Tarjan's algorithm for finding articulation points. This algorithm will find every vertex that, when removed from the graph, splits the graph in more than one connected component (these vertices are called articulation points). It's easy to see that there won't be isolated vertices if you remove a vertex that is not an articulation point. However, if you remove an articulation point, you will split the graph in two parts G and G', where G is the connected component of the starting vertex, and G' is the rest of the graph. All vertices from G' are isolated because you can't reach them if you run a DFS from the starting vertex. I think that you can find the size of G' for each vertex deletion efficiently, maybe you can even do this while running Tarjan's. If i find a solution i can edit this answer later.
EDIT: i managed to solve the problem in O(N+M). I will give some hints so you can find the answer by yourself:
Every undirected graph can be decomposed in (not disjoint) sets of biconnected components: each biconnected component is a subset of the vertices of the graph where every vertex in this subset will remain connected even if you remove any vertex of the graph
Tarjan's O(N+M) algorithm to find bridges and articulation points can be altered in order to find the biconnected components, finding which vertices belong to each biconnected component, or which biconnected components contain each vertex
If you remove any vertex that is not an articulation point, answer for this vertex is obviously N-1
If you remove an articulation point, every vertex in the same biconnected component of the starting vertex will still be acessible, but you don't know about the other biconnected components. Don't worry, there is a way to find this efficiently
You can compress every graph G in a graph B of its biconnected components. The compression algorithm is simple: every biconnected component becomes a vertex in B, and you link biconnected components that share some articulation point. We can prove that the resulting graph B is a tree. You must use this tree somehow in order to solve the problem presented in step 4
Good luck!
Here is the full question:
Assume we have a directed graph G = (V,E), we want to find a graph G' = (V,E') that has the following properties:
G' has same connected components as G
G' has same component graph as G
E' is minimized. That is, E' is as small as possible.
Here is what I got:
First, run the strongly connected components algorithm. Now we have the strongly connected components. Now go to each strong connected component and within that SCC make a simple cycle; that is, a cycle where the only nodes that are repeated are the start/finish nodes. This will minimize the edges within each SCC.
Now, we need to minimize the edges between the SCCs. Alas, I can't think of a way of doing this.
My 2 questions are: (1) Does the algorithm prior to the part about minimizing edges between SCCs sound right? (2) How does one go about minimizing the edges between SCCs.
For (2), I know that this is equivalent to minimizing the number of edges in a DAG. (Think of the SCCs as the vertices). But this doesn't seem to help me.
The algorithm seems right, as long as you allow for closed walks (i.e. repeating vertices.) Proper cycles might not exist (e.g. in an "8" shaped component) and finding them is NP-hard.
It seems that it is sufficient to group the inter-component edges by ordered pairs of components they connect and leave only one edge in each group.
Regarding the step 2,minimize the edges between the SCCs, you could randomly select a vertex, and run DFS, only keeping the longest path for each pair of (root, end), while removing other paths. Store all the vertices searched in a list L.
Choose another vertex, if it exists in L, skip to the next vertex; if not, repeat the procedure above.
I have a set of nodes and set of directed edges between them. The edges have no weight.
How can I found minimal number of edges which has to be added to make the graph strongly connected (ie. there should be a path from every node to all others)? Does this problem have a name?
It's a really classical graph problem.
Run algorithm like Tarjan-SCC algorithm to find all SCCs. Consider
each SCC as a new vertice, link a edge between these new
vertices according to the origin graph, we can get a new graph.
Obviously, the new graph is a Directed Acyclic Graph(DAG).
In the DAG, find all vertices whose in-degree is 0, we define them
{X}; find all vertices whose out-degree is 0, we define
them {Y}.
If DAG has only one vertice, the answer is 0; otherwise, the answer
is max(|X|, |Y|).
Off the top of my head, it seems the simplest (fewest edges) way to make a directed graph strongly connected would be to just have a cycle involving all nodes; so the minimum number of edges would just be N where N is the number of nodes. If there are already edges, just do something like connect longest existing directed path to the next longest path that doesn't overlap with your current path, until you form a complete cycle (once your path contains all nodes, connect the ends to form the cycle.)
Not sure if there is a more formal definition of any of this, but is seems logical to me.
I would find all weakly connected components, and tie them up in a cycle.
EDIT:
To be more explicit, the idea is if you have WCCs W(1),...,W(n),
make all of W(i%n + 1) reachable from any node in W(i), for i=1 to n.
Here are two excises about safe vertex deletions
5-28. An articulation vertex of a graph G is a vertex whose deletion disconnects G. Let G be a graph with n vertices and m edges. Give a simple O(n + m) algorithm for finding a vertex of G that is not an articulation vertex—i.e. , whose deletion does not disconnect G.
5-29. Following up on the previous problem, give an O(n + m) algorithm that finds a deletion order for the n vertices such that no deletion disconnects the graph. (Hint: think DFS/BFS.)
For 5-28, here is my thought:
I will just do a dfs, but not complete. The very first vertex which finished being processed will be a non-articulation vertex as it must be a leaf, or a leaf with a back edge pointing back to its ancestor (it is also not a articulation vertex).
For 5-29
I am not yet sure how to do it nicely. What comes into my mind is that in the graph, any vertex in a cycle can't deleted safely. Also, if there is no cycle, then deleting vertex backwards up from a dfs tree is also safe.
Could anyone give me some hints or tell me whether my thinking is correct or wrong?
I think your solution to 5-28 is correct, it guarantees to find an node which is not articulation in O(n+m) time.
For 5-29, I think one way to do it is based on your solution to 5-28. While doing dfs, keep tract of when did each node leaves the stack(the time finished being processed). As you said, the one leaves first must be a leaf node so delete it will not disconnect the graph. Then you can delete the node leaves the stack secondly, it must also be a leaf node when we removed the first node. So we can delete the nodes in the reverse order of when they are popped from stack while doing DFS. Doing this only need one pass of DFS, thus running time is O(n+m).
Another simple way is to do it with BFS. For 5.28, deleting any node with maximum depth will not make the graph disconnect. Because each other nodes can be reached by a node with less depth. So for 5.29, we can delete all nodes by their sort depth in descending order. And also, we only need 1 BFS so the running time is O(n+m). I think it's easier for people to understand this approach.
5-29:
Expanding on your idea from 5-28, when you finish processing a vertex, it is a non-articulation vertex, so delete it. Then continue the DFS, and every time you finish processing another vertex, delete it too. Since you deleted the previous vertices that finished processing, every time you finish processing a vertex it is actually the first time you finish processing a vertex (for the graph without the previously deleted ones).
Another method, easier to prove, and a bit less efficient (but still O(V + E)) - Crete a DFS tree from the graph, then do topological sorting, then remove the vertices one by one starting from the last one in the sorted graph and moving back to the first one. At every step you remove the last one, and you know for sure (because it's a topological sorted graph) that it doesn't point to any other node, meaning no edges will be deleted except the edges leading to it. That means all other nodes are still reachable from the first node, and if the graph were bi-directional, then all nodes can reach the first node too, making it connected.
For the first problem, I will just delete the vertex you want to test from the graph and then run a DFS/BFS starting from any other vertex, counting the number of visited vertices. If it's less than (original size - 1), then the tested vertex is an articulation vertex.
Same idea applies to the second problem. You randomly pick a vertex and delete it, which will in general cut the graph into two blocks. If the deleted vertex is not an articulation vertex, then one of the two blocks must be empty. Otherwise, both blocks have some vertices, in which case, all vertices in BOTH blocks have to be listed in front of this vertex in the final "safe-deletion" order, while it is not important to decide which block to be completely removed first. So we can write a little recursive function like this:
vertex[] safe_order_cut (vertex[] v)
if (v.length==0) return empty_vertex_list;
vertex x = randomly_pick(v);
vertex v1[], v2[];
cut_graph(v,x,v1,v2);
return safe_order_cut(v1) + safe_order_cut(v2) + x;
The connectivity problem (and related cut vertex problems) has been extensively studied in graph theory. If you are interested, you can read the wiki pages for more algorithms.
I have a directed cyclic graph. Some edges are FIXED and may not be removed. The other edges may be removed to break a cycle.
What is the best way to do remove the cycles in this graph?
The traversal should be as much as a DFS as possible and start at a given node.
What you can do is use Dijkstra's algorithm: start with a graph containing only the FIXED edges. Then apply an adaptation of the algorithm starting with the graph you already have:
Start with the starting node, and all FIXED edges in the component of the starting node. Assume this is a tree.
Add the node closest to the tree.
Also add any FIXED edges in the component of the node you just added.
If all nodes are in the tree, end. Otherwise, go to step 4.
This, of course, assumes that the graph consisting only of the FIXED edges does not contain cycles. If it does, there is no solution (that is, a subgraph without edges, but containing all FIXED edges).
For directed graphs, it's a bit more complicated. In this case, any component of the graph of FIXED edges should be a tree. In the Dijkstra-like algorithm, only roots of these nodes should be candidates to be added to the tree.
the problem is understated because you haven't specified e.g. if the graph needs to remain connected, or if you want to remove a "small" number of non-FIXED edges to break all cycles, or if you really need the globally minimum number of non-FIXED edges to be removed.
If the graph does not need to remain connected, just traverse all the edges and remove all non-FIXED ones. That removes all cycles which you can remove without removing FIXED edges.
If you want a simple greedy algorithm to remove edges that is pure DFS, you can use something like this IF the graph remains connected also when you remove some of the non-FIXED edges:
proc recurse(vertex n, vertex_set ns)
if (n appers_in ns) // it is a cycle
return BREAK_CYCLE
else for (e in list_outgoing_edges_from(n))
np = e.destination
result = recurse(np, add_to_set(ns, np))
if (result == BREAK_CYCLE)
if (e.FIXED)
return BREAK_CYCLE
else
[remove e from the graph]
return RETRY
else if (result == RETRY)
return recurse(n, ns)
return FINISHED
if (recurse (your_initial_node, empty_vertex_set()))
[graph contains a cycle through only FIXED edges]
else
[the reachable component from initial_node has no longer cycles]
I used the following algorithm to solve my problem:
Start with a graph of all fixed edges marked as confirmed
From a start node, recurse through all confirmed edges as well as the not-yet-confirmed ones. But when you're about to recurse down a not-yet-confirmed edge first check if the node that the edge goes to has a path, by following confirmed edges, to a node in the current search tree (i.e. a node with a visiting flag set). This check must be done recursively by following all confirmed edges but this is too slow for me so I will settle here to just check if the node is visiting or if any of the nodes it is confirmed connected to are visiting. This will cover most of my cases but occationally leave cycles in the graph.
After the above step I use Tarjan's algorithm for finding the strongly connected components left in the graph (this can be done in O(V + E) time). The number of strongly connected components will be very small in my cases so I just go through each strongly connected component and remove one removable edge each. Then I do this step again until no more cycles remain in the graph.
This works fine and is fast enough.