-3

I am trying to make the Dijkstra algorithm to take input from the text file. I found this example (https://www.programiz.com/dsa/dijkstra-algorithm) for Dijkstra, which does exactly what I need - finds the shortest path between two nodes and prints it together with a cost.

How can I fill the graph from the given text file? Thanks in advance for any replies.

Code example, updated to use decimal weights:

// Dijkstra's Algorithm in C++

#include <iostream>
#include <vector>
#include <limits.h>

using namespace std;

void DijkstrasTest();

int main() {
  DijkstrasTest();
  return 0;
}

class Node;
class Edge;

void Dijkstras();
vector<Node*>* AdjacentRemainingNodes(Node* node);
Node* ExtractSmallest(vector<Node*>& nodes);
double Distance(Node* node1, Node* node2);
bool Contains(vector<Node*>& nodes, Node* node);
void PrintShortestRouteTo(Node* destination);

vector<Node*> nodes;
vector<Edge*> edges;

class Node {
   public:
  Node(char id)
    : id(id), previous(NULL), distanceFromStart(INT_MAX) {
    nodes.push_back(this);
  }

   public:
  char id;
  Node* previous;
  double distanceFromStart;
};

class Edge {
   public:
  Edge(Node* node1, Node* node2, double distance)
    : node1(node1), node2(node2), distance(distance) {
    edges.push_back(this);
  }
  bool Connects(Node* node1, Node* node2) {
    return (
      (node1 == this->node1 &&
       node2 == this->node2) ||
      (node1 == this->node2 &&
       node2 == this->node1));
  }

   public:
  Node* node1;
  Node* node2;
  double distance;
};

///////////////////
void DijkstrasTest() {
  Node* a = new Node('a');
  Node* b = new Node('b');
  Node* c = new Node('c');
  Node* d = new Node('d');
  Node* e = new Node('e');
  Node* f = new Node('f');
  Node* g = new Node('g');

  Edge* e1 = new Edge(a, c, 1.74);
  Edge* e2 = new Edge(a, d, 2.156);
  Edge* e3 = new Edge(b, c, 2.516);
  Edge* e4 = new Edge(c, d, 1.321);
  Edge* e5 = new Edge(b, f, 3.51);
  Edge* e6 = new Edge(c, e, 3.11);
  Edge* e7 = new Edge(e, f, 2.2);
  Edge* e8 = new Edge(d, g, 1.1);
  Edge* e9 = new Edge(g, f, 1.5);

  a->distanceFromStart = 0;  // set start node
  Dijkstras();
  PrintShortestRouteTo(f);
}

///////////////////

void Dijkstras() {
  while (nodes.size() > 0) {
    Node* smallest = ExtractSmallest(nodes);
    vector<Node*>* adjacentNodes =
      AdjacentRemainingNodes(smallest);

    const int size = adjacentNodes->size();
    for (int i = 0; i < size; ++i) {
      Node* adjacent = adjacentNodes->at(i);
      double distance = Distance(smallest, adjacent) +
               smallest->distanceFromStart;

      if (distance < adjacent->distanceFromStart) {
        adjacent->distanceFromStart = distance;
        adjacent->previous = smallest;
      }
    }
    delete adjacentNodes;
  }
}

// Find the node with the smallest distance,
// remove it, and return it.
Node* ExtractSmallest(vector<Node*>& nodes) {
  int size = nodes.size();
  if (size == 0) return NULL;
  int smallestPosition = 0;
  Node* smallest = nodes.at(0);
  for (int i = 1; i < size; ++i) {
    Node* current = nodes.at(i);
    if (current->distanceFromStart <
      smallest->distanceFromStart) {
      smallest = current;
      smallestPosition = i;
    }
  }
  nodes.erase(nodes.begin() + smallestPosition);
  return smallest;
}

// Return all nodes adjacent to 'node' which are still
// in the 'nodes' collection.
vector<Node*>* AdjacentRemainingNodes(Node* node) {
  vector<Node*>* adjacentNodes = new vector<Node*>();
  const int size = edges.size();
  for (int i = 0; i < size; ++i) {
    Edge* edge = edges.at(i);
    Node* adjacent = NULL;
    if (edge->node1 == node) {
      adjacent = edge->node2;
    } else if (edge->node2 == node) {
      adjacent = edge->node1;
    }
    if (adjacent && Contains(nodes, adjacent)) {
      adjacentNodes->push_back(adjacent);
    }
  }
  return adjacentNodes;
}

// Return distance between two connected nodes
double Distance(Node* node1, Node* node2) {
  const int size = edges.size();
  for (int i = 0; i < size; ++i) {
    Edge* edge = edges.at(i);
    if (edge->Connects(node1, node2)) {
      return edge->distance;
    }
  }
  return -1;  // should never happen
}

// Does the 'nodes' vector contain 'node'
bool Contains(vector<Node*>& nodes, Node* node) {
  const int size = nodes.size();
  for (int i = 0; i < size; ++i) {
    if (node == nodes.at(i)) {
      return true;
    }
  }
  return false;
}

///////////////////

void PrintShortestRouteTo(Node* destination) {
  Node* previous = destination;
  cout << "Distance from start: "
     << destination->distanceFromStart << endl;
  while (previous) {
    cout << previous->id << " ";
    previous = previous->previous;
  }
  cout << endl;
}

// these two not needed
vector<Edge*>* AdjacentEdges(vector<Edge*>& Edges, Node* node);
void RemoveEdge(vector<Edge*>& Edges, Edge* edge);

vector<Edge*>* AdjacentEdges(vector<Edge*>& edges, Node* node) {
  vector<Edge*>* adjacentEdges = new vector<Edge*>();

  const int size = edges.size();
  for (int i = 0; i < size; ++i) {
    Edge* edge = edges.at(i);
    if (edge->node1 == node) {
      cout << "adjacent: " << edge->node2->id << endl;
      adjacentEdges->push_back(edge);
    } else if (edge->node2 == node) {
      cout << "adjacent: " << edge->node1->id << endl;
      adjacentEdges->push_back(edge);
    }
  }
  return adjacentEdges;
}

void RemoveEdge(vector<Edge*>& edges, Edge* edge) {
  vector<Edge*>::iterator it;
  for (it = edges.begin(); it < edges.end(); ++it) {
    if (*it == edge) {
      edges.erase(it);
      return;
    }
  }
}

Fragment of the input file, number of lines is unknown:

source_id,target_id,weight
1,2,0.17
2,3,0.13
3,4,0.15
3,5,0.02
4,5,0.09
5,2,0.01

ravenspoint
  • 19,093
  • 6
  • 57
  • 103
maso
  • 21
  • 5
  • 1
    What exactly is your question? What do you not understand? – Lukas-T Jun 05 '21 at 11:51
  • 1
    What does "struggling to read the file into the vector" mean? Do you have a ***specific*** technical questions on a programming topic? For general learning material related to basic C++ topics, such as reading files and using vectors: this is covered in every C++ textbook, with plenty of examples and tutorials; Stackoverflow is not a very good replacement for a C++ textbook. – Sam Varshavchik Jun 05 '21 at 11:55
  • Please note that Stack Overflow is a place for asking **specific** programming questions. You may want to read this for further information: [Why is “Can someone help me?” not an actual question?](https://meta.stackoverflow.com/q/284236/12149471) – Andreas Wenzel Jun 05 '21 at 12:02
  • Here are a lot of options to read a csv file: https://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c I'm not sure what your columns represent or how you would transform that data into Node objects, but perhaps it will get you started. – Retired Ninja Jun 05 '21 at 12:37
  • Thank you for your replies and sorry for misunderstanding. I tried to reformulate my post, hope it is better now. – maso Jun 05 '21 at 12:47

1 Answers1

1

Your input is a list of node links.

Your algorithm requires an adjacency matrix, which is a vector containing vectors of the nodes adjacent to each node.

To convert from a list of links to an adjacency matrix, you need a graph class that among other things will populate the adjacency martrix using a function such as AddLink( int src, int dst, double cost )

There are many such classes available for you to copy or modify to your particular situation. I'll mention just one: PathFinder

Below is the PathFinder code to do the conversion. You will need something slightly different to handle the format of your file. Here is the format that PathFinder is reading

while (std::getline(myFile, line))
    {
        std::cout << line << "\n";
        auto token = ParseSpaceDelimited(line);
        if (!token.size())
            continue;
        switch (token[0][0])
        {
        case 'g':
            if (myFinder.linkCount())
                throw std::runtime_error("cPathFinderReader:: g ( directed ) must occurr before any links");
            myFinder.directed();
            break;

        case 'l':
            if (weights && (token.size() != 4))
                throw std::runtime_error("cPathFinder::read bad link line");
            else if (3 > token.size() || token.size() > 4)
                throw std::runtime_error("cPathFinder::read bad link line");
            if (weights)
                cost = atof(token[3].c_str());
            else
                cost = 1;
            if (cost < maxNegCost)
                maxNegCost = cost;
            myFinder.addLink(
                token[1],
                token[2],
                cost);
            break;

        case 's':
            if (token.size() != 2)
                throw std::runtime_error("cPathFinder::read bad start line");
            myFinder.start(token[1]);
            break;

        case 'e':
            if (token.size() != 2)
                throw std::runtime_error("cPathFinder::read bad end line");
            myFinder.end(token[1]);
            break;
        }
    }
ravenspoint
  • 19,093
  • 6
  • 57
  • 103