Modifying data of custom data structure - c++

I have an input file that I'm trying to parse in order to perform operations like DFS, Best-First Search, A* search, etc.
It looks like this:
| The vertical bar denotes comments. Ignore.
|
| The first non-comment line contains N, the number of nodes in the graph.
5
| The second non-comment line contains the unique start node.
0
| The third non-comment line contains a single goal node.
3
| The fourth non-comment line specifies the number of heuristics.
1
| The fifth non-comment line starts the edge-cost adjacency matrix. [i,j] in the matrix represents the weight of the edge from node i to node j.
* 1 2 * *
* * 3 * *
* * * 5 4
* * * * *
* * * * *
| After the adjacency matrix, the heuristic evaluation vectors are given,
| where each row containing the heuristic value h(n) for a node n
3
6
1
0
For the code, I created a class for the nodes in the Adjacency list and a class for the graph. I have managed to parse all the input except the last one, the heuristic evaluation numbers. Here is my code:
class AdjListNode
{
private:
int v; // vertex or node number.
int weight; // weight on edge to reach a particular vertex.
int h; // a heuristic number for every node.
public:
AdjListNode(int _v, int _w) { v = _v; weight = _w;}
int getV() { return v;} // return the vertex number
int getWeight() {return weight;}
void setH(int _h) { h = _h;}
int getH() { return h;}
};
class Graph
{
private:
int V; // Total number of nodes/vertices in the graph
list<AdjListNode> *adj; // a list for storing all the nodes
//A helper function for Depth-First Search DFS.
bool DFSUtil(int v, bool visited[], int goal, int cost);
public:
Graph(int V);
void DFS(int v, int goal); // Depth First Search
void addEdge(int i, int j, char* val); // Add edge between i and j
// of weight val.
//Print the adjacency list;
void printList();
};
void Graph::printList()
{
list<AdjListNode>::iterator i;
for (int j = 0; j < V; j++)
{
for (i = adj[j].begin(); i != adj[j].end(); i++)
{
AdjListNode node = *i;
cout << node.getV() << " ";
}
cout << endl;
}
}
Graph::Graph(int V)
{
this->V = V;
adj = new list<AdjListNode>[V];
}
void Graph::addEdge(int u, int v, char* val)
{
int weight = atoi(val);
AdjListNode node(v, weight);
adj[u].push_back(node);
}
bool Graph::DFSUtil(int v, bool visited[], int goal, int cost)
{
visited[v] = true;
if (v == goal)
{
cout << "cost is : " << cost << endl;
cout << "path is : ";
return true;
}
list<AdjListNode>:: iterator i;
for (i = adj[v].begin(); i != adj[v].end(); i++)
{
AdjListNode node = *i;
if (!visited[node.getV()])
{
if (DFSUtil(node.getV(), visited, goal, cost + node.getWeight()))
{
cout << node.getV() << " " ;
return true;
}
}
}
return false;
}
void Graph::DFS(int v, int goal)
{
bool *visited = new bool[V];
for (int i = 0; i < V; i++)
visited[i] = false;
int cost = 0;
DFSUtil(v, visited, goal, cost);
}
Here is the main() function where I am trying to parse all the values from the input file:
int main()
{
string line; // read every line into this variable
int argCount = 0; // keep track of 4 arguments (V, start, goal, and
// J)
int V, start, goal, J; // V is number of nodes, start is the start
// node, goal is goal node, J is just a
// number (ignore for now)
//input for nodes, start, goal, and J
while (getline(cin, line) && argCount < 4)
{
if (line[0] == '|') // reject the line and continue if the line
continue; // starts with '|'
// convert the line which is a string into int and store in
// respective variables
switch (argCount)
{
case 0: V = stoi(line);
argCount++;
break;
case 1: start = stoi(line);
argCount++;
break;
case 2: goal = stoi(line);
argCount++;
break;
case 3: J = stoi(line);
argCount++;
break;
}
}
Graph g(V); // create a Graph of 'V' nodes or vertices.
//ignore lines starting with '|'
while (getline(cin, line))
{
if (line[0] == '|')
continue;
break;
}
// after the above loop, line contains the first row of the matrix
// buffer array for taking in weight from stringstream.
char weight[100];
//parse first row of the matrix
stringstream ss;
ss << line;
for (int v = 0; v < V; v++)
{
ss.getline(weight, 6, ' '); // get the value into weight,
// ' '(whitespace) is a delimiter.
if (weight[0] != '*') // ignore if weight is '*'
g.addEdge(0, v, weight);// call addEdge() to add edge
// between 0 (because this is the
// first row) and v and assign
// weight.
}
ss << ""; // reset stringstream.
ss.clear();
// parse rest of the rows of the matrix. first row already parsed
// above
for (int u = 1; u < V; u++) // u is row, starting from 1.
{ getline(cin, line); // take the whole line
ss.clear();
ss.str("");
ss << line; // put the line in stringstream
int v = 0; // v is column number.
while (ss >> weight)
{
if (weight[0] != '*')
g.addEdge(u, v, weight); // add Edge between u and v.
v++; // increment column number.
}
}
//ignore lines starting with '|' else set the h value for the nodes
// THIS IS WHERE I AM STUCK. I NEED TO TAKE IN THE H VALUES AND
// ASSOCIATE THEM WITH THEIR NODES WHICH ARE ALREADY CREATED.
int n = 0;
list<AdjListNode>::iterator i;
while (getline(cin, line))
{
//cout << line;
if (line[0] == '|')
continue;
int h = stoi(line);
}
cout << "The adjacency list is : " << endl;
g.printList();
g.DFS(start, goal);
cout << endl;
return 0;
}
How can I take in the h values and associate them with the correct nodes? I am confused because I am calling the addEdge() function to add the edges between the nodes before taking in the h values. I tried to use getters and setters but I am not able to implement those concepts because I am a novice.
Thanks!
EDIT 1 : Added comments.
EDIT 2 : The class structure is supposed to be this:
For the adjacency list node class, AdjListNode:
Each node has 3 attributes; an integer v to identify it, a weight for calculating the cost to reach that node, and a heuristic value h which I am confused on how to assign because I have already called the constructor and would be parsing the h values from the input file later.
I know I am missing some elementary concept here.
For the class Graph:
A list stores all the AdjListNode type nodes as an adjacency list. I need to perform A* search on this.

EDIT: The core of your difficulty here is found in your class structure. Think about your goal - you want to associate an h with an edge, correct? The word "associate" means that you need some sort of data structure in your class(es) that allows you to do just that.
There are a few structures to choose from: tuples and structs are two possibilities. If you store the structure in your list, you can enter all of edges, and then step through and enter all of the h values.
Another possibility is to use a union to allow you to store both types of values in the same list. This will be more difficult, but may accomplish your goal.
Beyond this, you will need to do the research yourself, here.
--
I know you're new here, so I'll let you know that very few people will just give you the correct code outright, especially on something this complex. Just a heads up.
Second, you're on rather shaky ground if you do not understand your own object structure. I'm having a hard time reading it myself, and I'm a professional. Please, for the love of all things digital, comment your code! What do your various one- or two-letter variables mean...or the ones with the longer but still elusive names? (Don't feel too bad about this...common habit among programmers. Ask anyone here.)
Getters and setters are not a complex matter. Getters simply return the value of a private variable in the class. A setter is a function that accepts an argument, and assigns that argument to a particular private variable in the class, usually after checking for errors.
The best advice I can give you right now is A) comment your code, and B) figure out your code's basic object structure. Literally write (on paper if you have to) the names of your objects, what is stored inside them, what's hidden and what's public, and what they're supposed to do. If you figure that out, you'll probably find your answer on your own. Otherwise, edit your question with that information.
If I see that work - the intended class structure and some good comments - I'll edit with more details on how to fix your code. It's about doing the footwork yourself.
By the way, you have variables hanging out above your scope declarations (in this case, public:) in your headers, which is a no-no.

Related

Shortest path in an unweighted graph, using user input in C++

As I was practicing C++ today, I came across a code which finds the shortest path in an unweighted graph using BFS and outputs the length of the path and the vertices it travelled through.
I attempted to change up this code by introducing user input.
First, the user has to input two integers, lets say N and M. N holds the number of vertices and M holds the number of edges.
The next M lines contain two integers, which indicates an undirected edge between two nodes.
I attempted to change the code in accordance to this, but I ran into a number of problems.
The first problem is that if I use gcc, the program will end after the for loop runs once in the main function.
However, if I use clang, the program runs fine. But there is an other issue and it relates to a very specific input.
The following input:
3 3
1 3
1 2
2 3
should return 1, but sometimes (specifically when I enter the input line by line) it returns the message: "Given source and destination are not connected". It is completely random.
The code is below:
#include <iostream>
#include <vector>
#include <list>
using namespace std;
bool BFS(vector<int> adjList[], int source, int dest, int numOfVertices, int pred[], int dist[]);
void printShortestDistance(vector<int> adjList[], int s, int dest, int numOfVertices);
int main()
{
int numOfVertices, numOfEdges;
cin >> numOfVertices >> numOfEdges;
vector<int> adjList[numOfVertices];
if (2 <= numOfVertices && numOfVertices <= 1e5 && 1 <= numOfEdges && numOfEdges <= 1e5)
{
for (int i = 0; i < numOfEdges; i++)
{
int node1, node2;
cin >> node1 >> node2;
if ((1 <= node1) && (1 <= node2) && (node1 <= numOfVertices) && (node2 <= numOfVertices) && (node1 != node2))
{
adjList[node1].push_back(node2);
adjList[node2].push_back(node1);
}
else{ return EXIT_FAILURE; }
}
int source = 1;
int dest = numOfVertices;
printShortestDistance(adjList, source, dest, numOfVertices);
}
}
void printShortestDistance(vector<int> adjList[], int source, int dest, int numOfVertices)
{
int pred[numOfVertices];
int dist[numOfVertices];
if (BFS(adjList, source, dest, numOfVertices, pred, dist) == false)
{
cout << "Given source and destination are not connected";
return;
}
vector<int> path;
int crawl = dest;
path.push_back(crawl);
while (pred[crawl] != -1)
{
path.push_back(pred[crawl]);
crawl = pred[crawl];
}
cout << "Shortest path length is : " << dist[dest];
cout << "\nPath is::\n";
for (int i = path.size() - 1; i >= 0; i--)
cout << path[i] << " ";
}
bool BFS(vector<int> adjList[], int source, int dest, int numOfVertices, int pred[], int dist[])
{
list<int> queue;
bool visited[numOfVertices];
for (int i = 0; i < numOfVertices; i++)
{
visited[i] = false;
dist[i] = INT_MAX;
pred[i] = -1;
}
visited[source] = true;
dist[source] = 0;
queue.push_back(source);
while (!queue.empty())
{
int u = queue.front();
queue.pop_front();
for (int i = 0; i < adjList[u].size(); i++)
{
if (visited[adjList[u][i]] == false)
{
visited[adjList[u][i]] = true;
dist[adjList[u][i]] = dist[u] + 1;
pred[adjList[u][i]] = u;
queue.push_back(adjList[u][i]);
// We stop BFS when we find
// destination.
if (adjList[u][i] == dest)
return true;
}
}
}
return false;
}
"First, the user has to input two integers, lets say N and M. N holds the number of vertices and M holds the number of edges."
This is not a good idea. Humans are terrible at counting, while computers are pretty good at it. So do not make your users count the vertices and edges - they will often get it wrong and cause chaos.
Just input the start and ending nodes of an edge. If a node is already present in the data structure, connect it. If node not already present then add it and then connect it.
You will have much happier users!
I see that you have decided to store your graph in an adjacency list. This is a perfectly reasonable idea, but the snag is that it is quite a challenge to code. This seems to be your first attempt to code a graph theory problem, so I recommend that you use an adjacency matrix instead since it is much easier to code.
For small graphs the difference is insignificant, so you need only consider switching to the more complicate adjacency list when you are working with enormous graphs ( many thousands of nodes )
Remember that vectors in C++ are 0-based.
In your example, numOfVertices and numOfEdges are both 3, so node 3 will lead to out-of-bound access. Either change your input to 0-based node numbers or use node1-1 and node2-1.
See also
Vector going out of bounds without giving error
and the accepted answer.

C++ If I sort a vector, how can I get another vector to align with that vector? [duplicate]

This question already has answers here:
Suppose we have two std::vectors v1 and v2 and we dont want to combine these in a struct. How to transform v2 the same way v1 was transformed by sort?
(3 answers)
Closed 1 year ago.
I created this little project in C++ for a basketball 'gameish' thing. One thing I wanted to implement was a funciton that allowed one to see who had the highest or lowest stats out of all of the players, in this case, specifically, points per game (ppg).
I broke up the stat components of the players and the names of the players into two different vectors, vector <vector <string> > names and vector <vector <double>> stats. I made it so that names[0] correlated to stats[0], so LeBron and his stats would be represented by say 1. I figured out how to sort the vectors by descending or ascending order, but when I sort a vector, say stats[0], the order is now different from the original order, and so it doesn't align with names[0]. So when I print stats[0][i] belongs to names[0][i] it no longer is true, as the two are no longer 'connected'. I've tried to troubleshoot this myself first, but came up empty, and then tried searching online for an answer which I haven't found, perhaps I didn't look hard enough. What do I do to make it so that the now mixed-up order of the stats vector can be written out correctly?
This code I created, seen below, works sometimes, for ppg it works until around 20. I tried to create it so that you could use any vector .
void sorting_test(bool ascend, int number, vector <double> statistic) {
vector <vector <double>> ppg_desc {
{},//ppg
{},//slot
{},//locked
};
//
for (int i = 0; i < statistic.size(); i++) {
ppg_desc[0].push_back(statistic[i]);
ppg_desc[1].push_back(i);
}
sort(ppg_desc[0].begin(), ppg_desc[0].end());
if (ascend == false)
reverse(ppg_desc[0].begin(), ppg_desc[0].end());
//
for (int i = 0; i < statistic.size(); i++) {
//
bool lok = false;
//
for (int f = 0; f < statistic.size(); f++) {
//
for (int d = 0; d < ppg_desc[2].size(); d++) {
//
if (i == ppg_desc[1][d]) {
//
lok = true;
//
for (int s = f + 1; s < statistic.size(); s++) {
//
if (statistic[s] == ppg_desc[0][i]) {
lok = false;
f = s;
if (f >= statistic.size()) f = statistic.size() - 1;
}
}
}
}
//
//if (stats[6][f] == ppg_desc[0][i] && lok == false) ppg_desc[1][i] = f, ppg_desc[2].push_back(ppg_desc[1][i]), cout<<i<<"-";
if (statistic[f] == ppg_desc[0][i] && lok == false) ppg_desc[1][i] = f, ppg_desc[2].push_back(i);
}
}
//
for (int i = 0; i < number; i++) {
cout << i << ".) " << ppg_desc[0][i] << " - " << names[0][ppg_desc[1][i]] << endl;
}
//
}
What you basically need is a list of the indices, sorted by one of the vectors content.
This can be achieved with a variant of std::sort taking function objects / lambdas; something like this:
std::vector<int> score = {2,4,3};
std::vector<std::string> names = {"a", "b", "c"};
std::vector<int> V(N);
std::iota(V.begin(),V.end(), 0); // fill V with values increasing by 1
std::sort(V.begin(),V.end(), [&](int i,int j){return score[i]<score[j];} );
std::cout << "Names sorted by score: " << names[V[0]] << ", "<< names[V[1]] << ", "<< names[V[2]] << "\n";
As you can see, you can use the indices in V, now sorted by score, as index into names.
See example at cpp.sh
Inspiration: https://stackoverflow.com/a/40183830
An alternative solution, as suggested in comments, would be to include all values that belong together (both names and scores in my example) into a struct, then have a vector of this struct, and then again use above sort method with a specialized sorting function.
struct Player { std::string name; int score; }
std::vector<Player> players;
//... insert some players
std::sort(players.begin(), players.end(), [](auto const & p1, auto const & p2) { return p1.score < p2.score; });
// .. and now all your data is sorted by score!

Shortest path with file as input

I'm creating a graph taking a file as input and I want to calculate the shortest path, to do so I used SPF algorithm. I have a few file I can use to see if it works, and here comes the problem because it works until I try it with the biggest one (which has over 1 million vertex and 2 million edges), considering that the second for dimension has about 700k vertex and 100 million edges and with it it works just fine, what do you think the problem is? I just need some hint I really can't figure it out!
Please be patient with me, I'm new in this comunity and to coding in general, I'm just trying to learn and understand things properly...
It's returning error 3221225725
// Function to compute the SPF algorithm
void shortestPathFaster(int S, int V)
{
// Create array d to store shortest distance
int d[V + 1];
// Boolean array to check if vertex
// is present in queue or not
bool inQueue[V + 1] = { false };
// Initialize the distance from source to
// other vertex as INT_MAX(infinite)
for (int i = 0; i <= V; i++) {
d[i] = INT_MAX;
}
d[S] = 0;
queue<int> q;
q.push(S);
inQueue[S] = true;
while (!q.empty()) {
// Take the front vertex from Queue
int u = q.front();
q.pop();
inQueue[u] = false;
// Relaxing all the adjacent edges of
// vertex taken from the Queue
for (int i = 0; i < graph[u].size(); i++) {
int v = graph[u][i].first;
int weight = graph[u][i].second;
if (d[v] > d[u] + weight) {
d[v] = d[u] + weight;
// Check if vertex v is in Queue or not
// if not then push it into the Queue
if (!inQueue[v]) {
q.push(v);
inQueue[v] = true;
}
}
}
}
// Print the result
print_distance(d, V);
}
The problem is most likely here:
int d[V + 1];
Firstly, variable length arrays are non-standard. Secondly, if V is large you will overflow the stack.
Solution: replace this with std::vector. bool inQueue[V + 1] should be treated similarly.
Also, replace char buffer[BUFFER_SIZE]; with std::string. You'll be glad you did.

Shortest path given beginning and end of a weighted graph

I have to read in a file to create a weighted graph. The shortest path must be found using input from the file. File will be structured as first line = numVertices and every line below that has 3 numbers given as : 0 1 2 where 0 is start of an edge, 1 is the end of the edge, and 2 is the weight. I've tried implementing an adjacency matrix when creating my graph but have had no luck. Any suggestions would be greatly appreciated.
Have tried restructuring the way I read in my file and have also tried adjusting my findShortestPath function.
void createGraph(string file_name) //Function to create a graph based on a file, whose name is file_name.
{
ifstream f(file_name);
if (f.fail())
{
return;
}
string line;
getline(f, line);
int num = stoi(line);
numVertices = num;
int data[50][50];
string line2;
int num1 = 0, num2 = 0, num3 = 0;
while (!f.eof())
{
f >> num1;
f >> num2;
f >> line2;
num3 = stoi(line2);
data[num1][num2] = num3;
}
}
//////////////////shortest path function
string findShortestPath(int start, int end)
{
int data[numVertices][numVertices];
int dist[numVertices];
bool sptSet[numVertices];
int parent[numVertices];
for (int i = 0; i < numVertices; i++)
{
parent[0] = -1;
dist[i] = INT_MAX;
sptSet[i] = false;
}
dist[start] = 0;
for (int count = 0; count < numVertices - 1; count++)
{
int u = minDistance(dist, sptSet);
sptSet[u] = true;
if (sptSet[u] == end)
break;
for (int v = 0; v < numVertices; v++)
if (!sptSet[v] && data[u][v] && dist[u] + data[u][v] < dist[v])
{
parent[numVertices] = end;
dist[v] = dist[u] + data[u][v];
}
}
printSolution(parent);
Output is not outputting shortest path and is printing random numbers.
In your findShortestPath function data[numVertices][numVertices] is not the same as the data variable in your createGraph function.
Look at this https://www.geeksforgeeks.org/scope-of-variables-in-c/ or try finding other sources on scope of variables.
In your function findShortestPath you have declared a local array called data, which is supposed to store the adjacency matrix data. However, you have never written any data into it! Consider passing the matrix as a function argument. Your function signature should look something like this:
findShortestPath(int** data, int numVertices, int start, int end)
Also, avoid using VLA as it is not part of the standard. Consider creating a two dimensional array on the heap using new. (and don't forget to do delete[] after you are done!)
Or you can use std::vector<std::vector<int>>, if you don't want to manage your array's lifetime manually.

How to represent a graph in c++

I would like to write prims and dijkstra algoritms that create a MST. But I do not know what is the best way to represent graph in c++.
I could represent an edge by pair of two ints for example vector 0 to 1 would be pair(0,1);
typedef pair<int, int> Edge;
And then the prims function would take Vector of pairs that consist of an edge and its weight.
void prims(vector<pair<Edge, int>>);
I think that this way is not the best one, could anyone tell me what way would be the best to represent a graph?
I have been implementing Dijkstra some time ago for finding paths in binary images. I represented a graph as a vector of a struct GraphNodes that contained a vector of Connections that contained all the connections of the node to other nodes. Each connection has its distance attribute, which is the weight of the edge. Here are the two structs I used:
//forward declaration
struct GraphNode;
struct Connection {
Connection() : distance(1) { };
Connection(GraphNode* ptr, double distance) : ptr(ptr), distance(distance) { };
bool operator==(const Connection &other) const;
GraphNode* ptr;
double distance;
};
struct GraphNode {
GraphNode() : connections(8), predecessor(NULL), distance(-1) { };
cv::Point point;
double distance;
GraphNode* predecessor;
std::vector<Connection> connections;
};
bool Connection::operator==(const Connection &other) const {
return ptr == other.ptr && distance == other.distance;
}
The distance attribute of the GraphNode is the distance it currently has in the Dijkstra algorithm, so the distance of the shortest currently known distance to the start node. At the beginning this is initialized with -1.
I then implemented the Dijkstra algorithm like this:
std::vector<cv::Point> findShortestPathDijkstra(std::vector<GraphNode>& graph, int startNodeIndex, int destNodeIndex) const {
GraphDistanceSorter sorter(graph);
std::set<GraphNode*, GraphDistanceSorter> unusedNodes(sorter);
for (int i = 0; i < graph.size(); ++i) {
unusedNodes.insert(&graph[i]);
}
while (unusedNodes.size() > 0) {
GraphNode* currentNode = *unusedNodes.begin();
if (currentNode->distance == -1) {
return std::vector<cv::Point>();
}
if (currentNode == &graph[destNodeIndex]) break;
unusedNodes.erase(currentNode);
//update distances of connected nodes
for (Connection const& con : currentNode->connections) {
/*here we could check if the element is really in unusedNodes (search, O(log n)), but this would
actually take longer than calculating the new distance (O(1)), which will in this case always be greater
than the old one, so the distance is never updated for nodes not in unusedNodes ()*/
double newDistance = currentNode->distance + con.distance;
if (newDistance < con.ptr->distance || con.ptr->distance == -1) {
unusedNodes.erase(con.ptr);
con.ptr->distance = newDistance;
con.ptr->predecessor = currentNode;
unusedNodes.insert(con.ptr);
}
}
}
//now trace back the path as a list of points
std::vector<cv::Point> points;
GraphNode* current = &graph[destNodeIndex];
points.push_back(current->point);
while (current != &graph[startNodeIndex]) {
if (current->predecessor == NULL) return std::vector<cv::Point>();
current = current->predecessor;
points.push_back(current->point);
}
return points;
}
As you see there is a set unusedNodes that contains all the unused nodes so far. It only contains pointers on graphNodes. The actual graph representation is in the vector. The advantage of having a set is, that it is always sorted according to a certain criterion. I implemented my own sorter GraphDistanceSorter that sorts the GraphNodes according to the distance criterion of the Dijkstra algorithm. This way I just have to pick the first node from the set and know that it's the one with the smallest distance:
struct GraphDistanceSorter {
bool operator() (const GraphNode* lhs, const GraphNode* rhs) const;
};
bool GraphDistanceSorter::operator() (const GraphNode* lhs, const GraphNode* rhs) const {
if (lhs->distance == rhs->distance) {
return lhs < rhs;
} else {
if (lhs->distance != -1 && rhs->distance != -1) {
if (lhs->distance != rhs->distance) {
return lhs->distance < rhs->distance;
}
} else if (lhs->distance != -1 && rhs->distance == -1) {
return true;
}
return false;
}
}
The two main ways to represent graphs learned in theoretical computer-science are adjacency matrix and adjacency lists.
Adjacency Matrix is as shown in the photo below is an n*n matrix and a[i][j] represents the edge between node i and node j so if it's a weighted graph it can be an integer instead of a boolean value for unweighted graphs.
adjacency matrix (photo source: google)
On the other hand, adjacency lists is a set of linked-lists (n-set to be exact), the i-th set has exactly the nodes i is connected to.
in this case you will need some additional way to save edge distance for example you can build your own class Edge as following
class Edge
{
int destination, length;
Edge* next = 0;
}
and use it for your linked-list. How ever I am used to std::vector<std::pair<int, int>> a[N] to define a list of pairs and a[i][j].first would be the j-th neighbor of nod i and a[i][j].second the length of the edge between them.
For undirected graph your can add i to j neighbors as well.
So it's also a flexible way to represent graphs.
adjacency lists (image source: google photos)
So now let's talk complexity, I will try to keep it as simple as possible:
We habe n lists, each has the #(edges going out of node i)
so the total number is sum of this numbers which is the total number of edges E.
That means place complexity is O(E) which is at most 5 * n in a sparse-graph in comparison to O(N^2) in adjacency matrix. (We need a linear factor of E to represent it).
Now let's consider visiting all neighbors of a nod x:
in adjacency matrix we will go through the whole x-th line and if it's not 0 there's an edge there which is O(N).
In adjacency lists it's again exactly the number of neighbors of x which can though reach O(N).
But if we are visiting all neighbors of all Nodes (which is the case in Dijkstra when updating dis array), you will need to visit n elements n times in adjacency lists which is also O(N^2) time complexity while in adjacency lists it's exactly the sum of the number of neighbors - again E. which means we need also O(E) to visit all neighbors of all edges.
And sind all edges are given usually in the input O(E) would pass as calculation time, but O(N^2) would be a high complexity for constraints of N <= 10^6 for example.
At the end I will leave you with my usual implementation of diffreent variants of graphs using adjacency lists (vector as a list):
#include<iostream>
#include<vector>
int main(){
const int N = 5;
int n, e;
std::vector<std::pair<int, int>> graph[N], inverse[N];
std::vector<int> unweighted[N], undirectedUnweighted[N];
std::cin >> n >> e;
for(int i = 0; i < e; i++)
{
int x, y, z;//z is length of edge
std::cin >> x >> y >> z;
//substitute 1 from x, y if they starts from 1
graph[x].push_back(std::make_pair(y, z));
inverse[y].push_back(std::make_pair(x, z));
unweighted[x].push_back(y);
undirectedUnweighted[x].push_back(y);
undirectedUnweighted[y].push_back(x);
}
return 0;
}
Simple form for representing graph (finding neighbors and degrees for vertices)
#include<iostream>
/** Representing graphs in c++ programming language */
using namespace std;
int main() {
cout << "\033[1;33mNote: if there are no neighbourhood between vertices write '-' symbol!\033[0m\n"<<endl;
int number_of_vertices;
cout<<"\033[1;32mPlease enter number of vertices: \033[0m";
cin>>number_of_vertices;
int max_num_of_neighbours;
cout<<"\033[1;32mPlease enter maximum number of neighbours: \033[0m";
cin>>max_num_of_neighbours;
char array[number_of_vertices][max_num_of_neighbours];
char vertices[number_of_vertices];
cout<<"\033[1;33mPlease sign vertices with lowercase alphabet letters: \033[0m"<<endl;
for(int i = 0; i < number_of_vertices; i ++) {
cout<<(i+1)<<" = ";
cin>>vertices[i];
}
for(int i = 0; i < number_of_vertices; cout<<endl, i ++) {
cout<<"\033[1;32mPlease enter neighbours for \033[0m"<<vertices[i]<<" --> ";
for(int j = 0; j < max_num_of_neighbours; j ++) {
cin>>array[i][j];
}
}
for(int i = 0; i < number_of_vertices; cout<<endl, i ++) {
cout<<"\033[1;34mNeighbours for \033[0m"<<"\033[1;35m"<<vertices[i]<<"\033[0m"<<" --> ";
int deg = 0;
for(int j = 0; j < max_num_of_neighbours; j ++) {
if(array[i][j] != '-') {
deg ++;
}
if(array[i][j] == '-') {
cout<<"\033[1;31m"<<array[i][j]<<"\033[0m"<<"\t";
} else {
cout<<"\033[1;32m"<<array[i][j]<<"\033[0m"<<"\t";
}
}
cout<<"\033[1;36m"<<"deg["<<"\033[0m"<<"\033[1;35m"<<vertices[i]<<"\033[0m"<<"\033[1;36m"<<"] = "<<"\033[0m"<<deg;
}
cout<<endl<<"\033[1;33mRemember that '\033[1;31m-\033[0m\033[1;33m' shows when two vertices aren't adjacent!\033[0m"<<endl;
}
For adding interactivity I used How do I output coloured text to a Linux terminal? for changing color of text