My suggestion:
First use Dijkstra to find the shortest path from Source to Destination, but walk from both the source and destination at the same time (use negative distance numbers to indicate how far away from the destination you have traveled), always expand the one with the shortest distance (from either source or destination). Once you encounter a node that has a value from the reverse node then, the path to get to that node is the shortest.
Then remove the edge, if the edge is not part of the shortest path then return the current known shortest path
If the removed edge is part of the shortest path then perform the search again with known absolute distance greater (positive or negative) than the lesser either of the nodes that was removed. Add the previously known shortest path to the known results as positive when walking from the start and negative when walking from the end up to the broken segment. Now search from that starting point in both directions, if you hit a node that has a value set (positive or negative), or was part of the previous shortest path, then you will have found your new shortest path.
The major benefit of doing this is that:
- you walk from both source and destination so unless your source node is by an edge you wander less nodes overall, and
- you don't abandon the entire search results, even if the edge removed was the very first edge in the previous shortest path, you just have to find the path to reconnect in the shortest way.
Performance over brute recalculation each time will be considerable, even when the removed node is part of the previously known shortest path.
For how it works, consider this graph:
I
/
B---E
/ / H
A D /|
\ / \ / |
C---F--G
We want to get from A to H, to make it easy lets assume each edge is worth 1 (but it could be anything)
We start at A:
I
/
B---E
/ / H
0 D /|
\ / \ / |
C---F--G
Now set the value for H to start at 0:
I
/
B---E
/ / (0)
0 D / |
\ / \ / |
C---F---G
And Expand:
I
/
1---E
/ / (0)
0 D / |
\ / \ / |
1---F---G
Now we expand the next lowest value which will be H:
I
/
1---E
/ / (0)
0 D / |
\ / \ / |
1---(-1)--(-1)
Now we arbitrary pick B because it comes before C, F or G (they have the same absolute value):
I
/
1---2
/ / (0)
0 D / |
\ / \ / |
1---(-1)--(-1)
Then C
I
/
1---2
/ / (0)
0 2 / |
\ / \ / |
1---2 & (-1)--(-1)
Now we have a node that knows both it's positive value and it's negative value, hence we know it's distance to both A and H and since we were expanding the shortest node first this must be the shortest path, therefore we can say that the shortest path from A to H is A->C->F->H and costs ABS(2)+ABS(-1) = 3
Now suppose we removed the line C->F
I
/
1---2
/ / (0)
0 2 / |
\ / \ / |
1 2 & (-1)--(-1)
We then remove all known values with an absolute value above the lesser value of C and F (in this case is 1) leaving:
I
/
1---E
/ / (0)
0 D / |
\ / \ / |
1 (-1)--(-1)
Now again we expand as before, starting with B:
I
/
1---2
/ / (0)
0 D / |
\ / \ / |
1 (-1)--(-1)
Then C
I
/
1---2
/ / (0)
0 2 / |
\ / \ / |
1 (-1)--(-1)
Now F:
I
/
1---2
/ / (0)
0 2&(-2) / |
\ / \ / |
1 (-1)---(-1)
Hence we know the shortest path from A to H is now: A->C->D->F->H and costs ABS(2)+ABS(-2) = 4
This will work with any number of nodes, edges and edge weights, in the event that you have no further nodes to expand then you return your "No Route" response.
You can further optimize it by not resetting the node values of nodes that were in the previous shortest path, in doing so you lose the simple nature but it's not overly complex.
In the above example it wouldn't make a difference initially, but it would make a difference if we then removed the linkage A->C because we would remember the costs of C and the other nodes in the chain (as negative)
The benefit over just using a single-sided Dijkstra and rolling back to before the removed edge can be shown below:
I
/
1---E
/ / H
0 D /|
\ / \ / |
1 F--G
Now we would expand B:
I
/
1---2
/ / H
0 D /|
\ / \ / |
1 F--G
C:
I
/
1---2
/ / H
0 2 /|
\ / \ / |
1 F--G
D:
I
/
1---2
/ / H
0 2 /|
\ / \ / |
1 3--G
E:
3
/
1---2
/ / H
0 2 /|
\ / \ / |
1 3--G
F:
3
/
1---2
/ / 4
0 2 /|
\ / \ / |
1 3--4
And then we would determine the path is now A->C->D->F->H and it costs 4. Notice we needed to do 5 expand steps here, compare that to the 3 that we needed for the by-directional way.
As the removed edge gets more towards the middle of the path we will get a greatly improved saving by using a bi-directional graph walking algorithm to recalculate the new path. Unless there's say 50 nodes hanging off H but only a single path all the way from A to H but that's a fringe case and unlikely to happen in a normal network, in the event that it does this will still work good on average, in contrast to the reverse where there is only one direct path from H to A but 50 edges attached to A.
Given you have some ~200,000 edges with potentially up to 200,000 nodes, you're likely going to see considerable savings compared to my example graph which has only 9 nodes and 11 edges. This is based on the idea that we're looking for the algorithm that has the fewest node expansions as they are likely where the majority of the computational time will be spent looping over.
Source and destination are fixed throughout the problem. There will be upto 200,000 queries for each edge removal. at a time , only one edge is removed from graph.- Pranalee