1

As title says, what I'm looking for is printing "all the shortest paths" that are tied by weight.

Example:

We have a graph with edges going from 0 -> 1 -> 3 that has 6 as weight, but we also have the path 0 -> 3 that has 6 as weight as well, the algorithm below only returns the first path, I would like to know if it is possible to return both or all the paths alike. Also is there a more efficient/elegant way of printing the shortest path. I took this code as example only, mine is very similar but prints only from source to last vertex.

There is a similar question answered here, but I could not understand the code since I'm familiar with c++.

#include <iostream>
#include <vector>
#include <iomanip>
#include <climits>
using namespace std;

// Data structure to store graph edges
struct Edge
{
    int source, dest, weight;
};

// Recurive Function to print path of given vertex v from source vertex
void printPath(vector<int> const &parent, int v)
{
    if (v < 0)
        return;

    printPath(parent, parent[v]);
    cout << v << " ";
}

// Function to run Bellman Ford Algorithm from given source
void BellmanFord(vector<Edge> const &edges, int source, int N)
{
    // count number of edges present in the graph
    int E = edges.size();

    // distance[] and parent[] stores shortest-path (least cost/path)
    // information. Initially all vertices except source vertex have
    // a weight of infinity and a no parent

    vector<int> distance (N, INT_MAX);
    distance[source] = 0;

    vector<int> parent (N, -1);

    int u, v, w, k = N;

    // Relaxation step (run V-1 times)
    while (--k)
    {
        for (int j = 0; j < E; j++)
        {
            // edge from u to v having weight w     
            u = edges[j].source, v = edges[j].dest;
            w = edges[j].weight;

            // if the distance to the destination v can be
            // shortened by taking the edge u-> v
            if (distance[u] != INT_MAX && distance[u] + w < distance[v])
            {
                // update distance to the new lower value
                distance[v] = distance[u] + w;

                // set v's parent as u
                parent[v] = u;
            }
        }
    }

    // Run Relaxation step once more for Nth time to
    // check for negative-weight cycles
    for (int i = 0; i < E; i++)
    {
        // edge from u to v having weight w
        u = edges[i].source, v = edges[i].dest;
        w = edges[i].weight;

        // if the distance to the destination u can be
        // shortened by taking the edge u-> v       
        if (distance[u] != INT_MAX && distance[u] + w < distance[v])
        {
            cout << "Negative Weight Cycle Found!!";
            return;
        }
    }

    for (int i = 0; i < N; i++)
    {
        cout << "Distance of vertex " << i << " from the source is "
             << setw(2) << distance[i] << ". It's path is [ ";
        printPath(parent, i); cout << "]" << '\n';
    }
}

// main function
int main()
{
    // vector of graph edges as per above diagram
    vector<Edge> edges =
    {
        // (x, y, w) -> edge from x to y having weight w
        { 0, 1, 2 }, { 1, 3, 4 }, { 0, 3, 6 }
    };

    // Set maximum number of nodes in the graph
    int N = 5;

    // let source be vertex 0
    int source = 0;

    // run Bellman Ford Algorithm from given source
    BellmanFord(edges, source, N);

    return 0;
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
Ryenra
  • 101
  • 4
  • Is it a duplicate of https://stackoverflow.com/questions/11369829/bellman-ford-all-shortest-paths. Point out as this post has more views as of today and we can merge these 2 posts for one location for all answers on the subject. – Singh Aug 01 '23 at 22:19

1 Answers1

0

Looking at this more abstractly, you have something that can find a smallest thing, and you want to change it to also return all the other things equally small. (The same principles apply when looking for largest things. Sticking to "smallest", though, lends itself to an easier explanation.)

Many algorithms for finding an extreme thing have some part where they ask "is A more extreme than B". For example, you might see code like the following:

if ( A < B )
    smallest = A;

Notice how this ignores ties (A == B) the same way it ignores worse results (A > B). Hence, you get only the first of the best results returned. So this is something to change. However, you cannot simply change A < B to A <= B, since that would replace B with A in the case of a tie, the same way it is replaced when A is a better result. (You'd get only the last of the best results returned.) The three cases (less than, equal to, and greater than) need to be dealt with separately.

Another aspect to look at is how the smallest thing is tracked. The above code snippet suggests that smallest has the same type as A; this is inadequate for tracking multiple solutions. You probably will want a container to track solutions. A vector is likely a reasonable choice.

Putting this together, the above code might become something more like the following (after changing the declaration of smallest):

if ( A < B ) {
    smallest.clear();
    smallest.push_back(A);
}
else if ( A == B ) {
    smallest.push_back(A);
}

How can this be applied to Bellman-Ford?

Fortunately the key part of the code is relatively easy since there are comments documenting it. The harder part is changing the code to track multiple results, as there are two pieces of data updated when a shorter path is found. It looks like parent is the data that needs to be expanded. Here is a new declaration for it:

vector< vector<int> > parent (N);

This uses an empty vector instead of -1 to indicate "no parent". The check for shortest path can now become

if (distance[u] != INT_MAX) {
    // if the distance to the destination v can be
    // shortened by taking the edge u-> v
    if (distance[u] + w < distance[v])
    {
        // update distance to the new lower value
        distance[v] = distance[u] + w;
        // forget the previous parent list.
        parent[v].clear();
    }
    // if u-> v is a way to get the shortest
    // distance to the destination v.
    if (distance[u] + w == distance[v])
    {
        // add u as a possible parent for v
        parent[v].push_back(u);
    }
}

This differs a little from what the general approach in that there is no "else". It is the same logic, just arranged a bit differently. Note that when the first if clause is entered, the distance vector is updated so the second if clause is entered as well.

I think that handling the found paths is a separate (and not trivial) question, so I'll leave it to you to figure out how to update printPath(). I will, though, give a version that preserves the old output (just the first of the shortest paths) while receiving the new results. This is not a recommendation so much as an illustration relating the new data structure to the old.

// Recursive function to print the path of (just) the first given vertex from source vertex.
void printPath(vector< vector<int> > const &parent, vector<int> const &vlist)
{
    if (vlist.empty())
        return;

    int v = vlist.front();
    printPath(parent, parent[v]);
    cout << v << " ";
}
JaMiT
  • 14,422
  • 4
  • 15
  • 31