views:

352

answers:

5

There is a directed graph (not necessarily connected) of which one or more nodes are distinguished as sources. Any node accessible from any one of the sources is considered 'lit'. Now suppose one of the edges is removed. The problem is to determine the nodes that were previously lit and are not lit anymore.

An analogy like city electricity system may be considered, I presume.

A: 

How big and how connected are the graphs? You could store all paths from the source nodes to all other nodes and look for nodes where all paths to that node contain one of the remove edges.

EDIT: Extend this description a bit

Do a DFS from each source node. Keep track of all paths generated to each node (as edges, not vertices, so then we only need to know the edges involved, not their order, and so we can use a bitmap). Keep a count for each node of the number of paths from source to node.

Now iterate over the paths. Remove any path that contains the removed edge(s) and decrement the counter for that node. If a node counter is decremented to zero, it was lit and now isn't.

Paul
Sorry, forgot to mention that problem size varies although i may say the responsiveness of the system is important; it is planned to be a web service.
tafa
+1  A: 

If instead of just "lit" or "unlit" you would keep a set of nodes from which a node is powered or lit, and consider a node with an empty set as "unlit" and a node with a non-empty set as "lit", then removing an edge would simply involve removing the source node from the target node's set.

EDIT: Forgot this: And if you remove the last lit-from-node in the set, traverse the edges and remove the node you just "unlit" from their set (and possibly traverse from there too, and so on)

EDIT2 (rephrase for tafa): Firstly: I misread the original question and thought that it stated that for each node it was already known to be lit or unlit, which as I re-read it now, was not mentioned.

However, if for each node in your network you store a set containing the nodes it was lit through, you can easily traverse the graph from the removed edge and fix up any lit/unlit references. So for example if we have nodes A,B,C,D like this: (lame attempt at ascii art)

A -> B >- D
 \-> C >-/

Then at node A you would store that it was a source (and thus lit by itself), and in both B and C you would store they were lit by A, and in D you would store that it was lit by both A and C.

Then say we remove the edge from B to D: In D we remove B from the lit-source-list, but it remains lit as it is still lit by A. Next say we remove the edge from A to C after that: A is removed from C's set, and thus C is no longer lit. We then go on to traverse the edges that originated at C, and remove C from D's set which is now also unlit. In this case we are done, but if the set was bigger, we'd just go on from D.

This algorithm will only ever visit the nodes that are directly affected by a removal or addition of an edge, and as such (apart from the extra storage needed at each node) should be close to optimal.

Frans-Willem
I am terribly sorry but I could not follow your proposal although I read it 3-4 times. Would you mind rephrasing it?
tafa
I rephrased and slightly altered the post in my second edit.
Frans-Willem
Ascii art is fine, and your answer is even finer, brought another perspective. Thanks.
tafa
+1  A: 

Is this your homework?

The simplest solution is to do a DFS (http://en.wikipedia.org/wiki/Depth-first_search) or a BFS (http://en.wikipedia.org/wiki/Breadth-first_search) on the original graph starting from the source nodes. This will get you all the original lit nodes.

Now remove the edge in question. Do again the DFS. You can the nodes which still remain lit.

Output the nodes that appear in the first set but not the second.

This is an asymptotically optimal algorithm, since you do two DFSs (or BFSs) which take O(n + m) times and space (where n = number of nodes, m = number of edges), which dominate the complexity. You need at least o(n + m) time and space to read the input, therefore the algorithm is optimal.

Now if you want to remove several edges, that would be interesting. In this case, we would be talking about dynamic data structures. Is this what you intended?

EDIT: Taking into account the comments:

  • not connected is not a problem, since nodes in unreachable connected components will not be reached during the search
  • there is a smart way to do the DFS or BFS from all nodes at once (I will describe BFS). You just have to put them all at the beginning on the stack/queue.

Pseudo code for a BFS which searches for all nodes reachable from any of the starting nodes:

Queue q = [all starting nodes]
while (q not empty)
{
   x = q.pop()
   forall (y neighbour of x) {
      if (y was not visited) {
         visited[y] = true
         q.push(y)
      }
   }
}

Replace Queue with a Stack and you get a sort of DFS.

DFS/BFS assumes a connected graph. Not the case in this problem.
Triptych
It is my job, actually.
tafa
Disconnected isn't a problem. Do a DFS from each source node?
Paul
Yeah a DFS from each source works, but it's so crappy I didn't bother posting it. I feel like there's something better.
Triptych
Don't know why this was marked down. Start a modified DFS at each source node. Mark each node you hit as lit. Ignore any nodes you meet that are already lit to hand cylces in the graph.
Shane MacLaughlin
Right, but you only need to do that once- afterwards you can iterate over the result. Hmm. If you discard nodes that aren't initially lit, one thing you're asking is "is this set of edges a cut? / is the graph disconnected". Not sure that helps any, though
Paul
It is the exact same feeling that made me post, since I already considered this solution before asking. But thanks anyway.
tafa
I have modified the answer to better explain how to compute the reachable set of nodes "in one step".
+7  A: 

This is a "dynamic graph reachability" problem. The following paper should be useful:

A fully dynamic reachability algorithm for directed graphs with an almost linear update time. Liam Roditty, Uri Zwick. Theory of Computing, 2002.

This gives an algorithm with O(m * sqrt(n))-time updates (amortized) and O(sqrt(n))-time queries on a possibly-cyclic graph (where m is the number of edges and n the number of nodes). If the graph is acyclic, this can be improved to O(m)-time updates (amortized) and O(n/log n)-time queries.

It's always possible you could do better than this given the specific structure of your problem, or by trading space for time.

Chris Conway
A: 

I would keep the information of connected source nodes on the edges while building the graph.(such as if edge has connectivity to the sources S1 and S2, its source list contains S1 and S2 ) And create the Nodes with the information of input edges and output edges. When an edge is removed, update the output edges of the target node of that edge by considering the input edges of the node. And traverse thru all the target nodes of the updated edges by using DFS or BFS. (In case of a cycle graph, consider marking). While updating the graph, it is also possible to find nodes without any edge that has source connection (lit->unlit nodes). However, it might not be a good solution, if you'd like to remove multiple edges at the same time since that may cause to traverse over same edges again and again.

hakan