Struct forward list items disapearing? - c++

This piece of code is getting quite on my nerves. Been debugging it for a while, cant believe how rusty i've got on c++.
I'm trying to model a graph to run some simple algorithms on, but that doesn't seem to work out so well. Every vertex contains a forward list to his neighbors, however when inserting the elements their obviously present.. Until I reach the print function; at that time the forward list is empty.
I've tried to allocate the forward_list using new aswell, as scoping could be an explication for it.. No luck there either..
#include <iostream>
#include <vector>
#include <set>
#include <forward_list>
#include <fstream>
using namespace std;
typedef struct Vertex Vertex;
struct Vertex {
unsigned id;
forward_list<Vertex*>_next;
bool operator < (const Vertex &other) const { return id < other.id; };
};
typedef set<Vertex> Graph;
typedef vector<Vertex*> Index;
typedef pair<unsigned, unsigned> Edge;
typedef forward_list<Vertex*> Neighbors;
// Function: process_line()
// Purpose: process a specific line from the file.
// Params: line to process
Edge process_line(string line){
unsigned vertex_from;
unsigned vertex_to;
int idx = line.find(" ");
vertex_from = (unsigned)stoul(line.substr(0, idx));
vertex_to = (unsigned)stoul(line.substr(idx+1, line.length()));
return make_pair(vertex_from, vertex_to);
}
// Function: load_graph()
// Purpose: load graph from file in relation
// Params: path, and reference to graph and index
bool load_graph(string file_path, Graph &graph, Index &index){
string line;
ifstream file(file_path);
bool foundEmptyLine = false;
if(file.is_open()){
while(getline(file, line)){
if(line.empty()){
foundEmptyLine = true;
continue;
}
if(!foundEmptyLine){
// processing vertexes
Vertex *vertex = new Vertex;
vertex->id = stoul(line);
graph.insert(*vertex);
index.emplace_back(vertex);
}else{
//Processing relations
Edge edge = process_line(line);
Vertex* neighbor = index.at(edge.second);
Vertex* source = index.at(edge.first);
// Lookup edge in index
source->_next.emplace_front(neighbor);
// ITEMS PRESENT! <----------------------
}
}
file.close();
}else{
cout << "Unable to open " << file_path;
return false;
}
return true;
}
void print_graph(Graph &graph){
for(Graph::iterator it = graph.begin(); it != graph.end(); ++it){
Neighbors neighs = it->_next;
cout << "Node: " << it->id << " neighbors: " neighs.empty();
cout << endl;
}
}
// Entry point.
int main() {
Graph graph;
Index index;
load_graph("graph_1.txt", graph, index);
print_graph(graph);
}

This is once again the same problem as yesterday.
Let's try to recapitulate the std::set
Since C++11 the iterator of a std::set is always an iterator to const value_type. This is because when we change an entry of a std::set this entry would need to be placed somewhere else in the data structure.
When we insert something into a std::set, two signatures are provided:
pair<iterator,bool> insert (const value_type& val);
pair<iterator,bool> insert (value_type&& val);
But in any case the insertion copies or moves the element into the container.
So in your case when you do
Vertex *vertex = new Vertex;
vertex->id = stoul(line);
graph.insert(*vertex);
index.emplace_back(vertex);
First you allocate memory (which by the way you never delete! You will leak much memory, which you can check using valgrind). Then you insert a copy of your vertex into the std::set and insert the pointer of your allocated memory into the std::vector.
When you then later do
Vertex* neighbor = index.at(edge.second);
Vertex* source = index.at(edge.first);
// Lookup edge in index
source->_next.emplace_front(neighbor);
You take the Vertex from your vector (remember, this is the vertex which you allocated with new). And insert another vertex (also dynamically allocated) into its std::forward_list. But: They have nothing to do with the vertex which are in your std::set.
So when you then later go through your std::set:
for (Graph::iterator it = graph.begin(); it != graph.end(); ++it)
This is completely unrelated to what you did when when inserting the edges - and all std::forward_lists are empty.
Side notes:
This is something you had to use in C, but not in C++!
typedef struct Vertex Vertex;
This one you should place above:
typedef forward_list<Vertex*> Neighbors;
It doesn't make sense to declare the type of Neighbors after you declared _next, because _next has this type.
Use const whereever you can, and cbegin / cend whereever you can (I already told you this yesterday), e.g.:
for(Graph::iterator it = graph.cbegin(); it != graph.cend(); ++it){
It doesn't make a difference here, but if you change the type of graph at some point, begin() may return a iterator to value_type instead of const value_type

Modified the graph to keep references to the existing vertices. I'm still not sure why this fixed it---but felt like giving a heads-up.

Related

How to get an element (struct) in an array by a value in the struct

Let's say I have this struct containing an integer.
struct Element
{
int number;
Element(int number)
{
this->number = number;
}
};
And I'm gonna create a vector containing many Element structs.
std::vector<Element> array;
Pretend that all the Element structs inside array have been initialized and have their number variable set.
My question is how can I instantly get an element based on the variable number?
It is very possible to do it with a for loop, but I'm currently focusing on optimization and trying to avoid as many for loops as possible.
I want it to be as instant as getting by index:
Element wanted_element = array[wanted_number]
There must be some kind of overloading stuff, but I don't really know what operators or stuff to overload.
Any help is appreciated :)
With comparator overloading implemented, std::find is available to help:
#include <iostream>
#include <vector>
#include <algorithm>
struct Element
{
int number;
Element(int number)
{
this->number = number;
}
bool operator == (Element el)
{
return number == el.number;
}
};
int main()
{
std::vector<Element> array;
std::vector<int> test;
for(int i=0;i<100;i++)
{
auto t = clock();
test.push_back(t);
array.push_back(Element(t));
}
auto valToFind = test[test.size()/2];
std::cout << "value to find: "<<valToFind<<std::endl;
Element toFind(valToFind);
auto it = std::find(array.begin(),array.end(),toFind);
if(it != array.end())
std::cout<<"found:" << it->number <<std::endl;
return 0;
}
The performance on above method depends on the position of the searched value in the array. Non-existing values & last element values will take the highest time while first element will be found quickest.
If you need to optimize searching-time, you can use another data-structure instead of vector. For example, std::map is simple to use here and fast on average (compared to latest elements of vector-version):
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
struct Element
{
int number;
Element(){ number = -1; }
Element(int number)
{
this->number = number;
}
};
int main()
{
std::map<int,Element> mp;
std::vector<int> test;
for(int i=0;i<100;i++)
{
auto t = clock();
test.push_back(t);
mp[t]=Element(t);
}
auto valToFind = test[test.size()/2];
std::cout << "value to find: "<<valToFind<<std::endl;
auto it = mp.find(valToFind);
if(it != mp.end())
std::cout<<"found:" << it->second.number <<std::endl;
return 0;
}
If you have to use vector, you can still use the map near the vector to keep track of its elements the same way above method just with extra memory space & extra deletions/updates on the map whenever vector is altered.
Anything you invent would with success would look like hashing or a tree in the end. std::unordered_map uses hashing while std::map uses red-black tree.
If range of values are very limited, like 0-to-1000 only, then simply saving its index in a second vector would be enough:
vec[number] = indexOfVector;
Element found = array[vec[number]];
If range is full and if you don't want to use any map nor unordered_map, you can still use a direct-mapped caching on the std::find method. On average, simple caching should decrease total time taken on duplicated searches (how often you search same item?).

String Searching Algorithm that uses a graph ? C++

Code Instructions
Hey guys. Above is a coding project I have been assigned. Im reading the instructions and am completely lost because I've never learned how to code an undirected graph? Not sure how my professor expects us to do this but I was hoping I could get some help from experts. Any readings (or tips) you guys suggest I can look at to familiarize myself with how to get started on the program? Appreciate it, thanks!
The problem to solve is called "Word Morph". Your instructor gave some restrictions as to use an undirected graph, where the neighbour node differs only one character from the origin. Unfortuantely the requirements are not clear enough. "Differ by one character is ambiguous. If we use the replace-insert-delete idiom, then we can use other functions as by comparing 2 equal size strings. I assume the full approach.
And, at the end, you need to find a shortest way through a graph.
I could present you one possible solution. A complete working code example.
By the way the graph is non-weigthed, because the cost of travelling from one node to the next is always 1. So actually we are talking about an undirected non-weighted graph.
The main algorithms we need using here are:
Levensthein. Calculate distance of 2 strings
and Breadth First Search, to find the shortes path through a graph
Please note, If the words should have the same length, then no Levensthein is needed. Just compare character by charcter and count the differences. That's rather simple. (But as said: The instructions are a little bit unclear)
Both algorithms can be modified. For example: You do not need a Levensthein distance greater than 1. You can terminate the distance calculation after distance one has been found. And, in the breadth first search, you could show the path, through which you are going.
OK, now, how to implement an undirected graph. There are 2 possibilities:
A Matrix (I will not explain)
A list or a vector
I would recommend the vector approach for this case. A matrix would be rather sparse, so, better a vector.
The basic data structure that you need is a node containing vertices and neighbours. So you have the word (a std::string) as vertex and the "neighbours". That is a std::vector of index positions to other nodes in the graph.
The graph is a vector of nodes. And nodes neighbours point to other nodes in this vector. We use the index into the vector to denote neighbours. All this we pack into a structure and call it "UndirectedGraph". We add a "build" function that checks for adjacencies. In this function we compare each string with any and check, if the difference is <2, so 0 or 1. 0 means equeal and 1 is a given constraint. If we find such a difference, we add it as neighboour in the corresponding node.
Additionally we add a breadth first search algorithm. It is described in Wikipedia
To ease up the implementation of that algortuhm we a a "visited" flag to the node.
Please see the code below:
#include <sstream>
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <iomanip>
#include <numeric>
#include <algorithm>
#include <queue>
std::istringstream textFileStream(R"#(peach
peace
place
plane
plans
plays
slays
stays
stars
sears
years
yearn
)#");
using Vertex = std::string;
using Edge = std::vector<size_t>;
// One node in a graph
struct Node
{
// The Vertex is a std::string
Vertex word{};
// The edges are the index of the neighbour nodes
Edge neighbour{};
// For Breath First Search
bool visited{ false };
// Easy input and output
friend std::istream& operator >> (std::istream& is, Node& n) {
n.neighbour.clear();
std::getline(is, n.word);
return is;
}
friend std::ostream& operator << (std::ostream& os, const Node& n) {
os << std::left << std::setw(25) << n.word << " --> ";
std::copy(n.neighbour.begin(), n.neighbour.end(), std::ostream_iterator<int>(os, " "));
return os;
}
};
// The graph
struct UndirectedGraph
{
// Contains a vector of nodes
std::vector<Node> graph;
// build adjacenies
void build();
// Find Path
bool checkForPathFromStartToEnd(size_t start, size_t end);
bool checkForPath() {bool result = false;if (graph.size() > 1) {size_t s = graph.size() - 2;size_t e = s + 1;result = checkForPathFromStartToEnd(s, e); }return result; }
// Easy input and output
friend std::istream& operator >> (std::istream& is, UndirectedGraph& ug) {
ug.graph.clear();
std::copy(std::istream_iterator<Node>(is), std::istream_iterator<Node>(), std::back_inserter(ug.graph));
return is;
}
friend std::ostream& operator << (std::ostream& os, const UndirectedGraph& ug) {
size_t i{ 0 };
for (const Node& n : ug.graph)
os << std::right << std::setw(4) << i++ << ' ' << n << '\n';
return os;
}
};
// Distance between 2 strings
size_t levensthein(const std::string& string1, const std::string& string2)
{
const size_t lengthString1(string1.size());
const size_t lengthString2(string2.size());
if (lengthString1 == 0) return lengthString2;
if (lengthString2 == 0) return lengthString1;
std::vector<size_t> costs(lengthString2 + 1);
std::iota(costs.begin(), costs.end(), 0);
for (size_t indexString1 = 0; indexString1 < lengthString1; ++indexString1) {
costs[0] = indexString1 + 1;
size_t corner = indexString1;
for (size_t indexString2 = 0; indexString2 < lengthString2; ++indexString2) {
size_t upper = costs[indexString2 + 1];
if (string1[indexString1] == string2[indexString2]) {
costs[indexString2 + 1] = corner;
}
else {
const size_t temp = std::min(upper, corner);
costs[indexString2 + 1] = std::min(costs[indexString2], temp) + 1;
}
corner = upper;
}
}
size_t result = costs[lengthString2];
return result;
}
// Build the adjacenies
void UndirectedGraph::build()
{
// Iterate over all words in the graph
for (size_t i = 0; i < graph.size(); ++i)
// COmpare everything with everything (becuase of symmetries, omit half of comparisons)
for (size_t j = i + 1; j < graph.size(); ++j)
// Chec distance of the 2 words to compare
if (levensthein(graph[i].word, graph[j].word) < 2U) {
// And store the adjacenies
graph[i].neighbour.push_back(j);
graph[j].neighbour.push_back(i);
}
}
bool UndirectedGraph::checkForPathFromStartToEnd(size_t start, size_t end)
{
// Assume that it will not work
bool result = false;
// Store intermediate tries in queue
std::queue<size_t> check{};
// Set initial values
graph[start].visited = true;
check.push(start);
// As long as we have not visited all possible nodes
while (!check.empty()) {
// Get the next node to check
size_t currentNode = check.front(); check.pop();
// If we found the solution . . .
if (currentNode == end) {
// The set resultung value and stop searching
result = true;
break;
}
// Go through all neighbours of the current node
for (const size_t next : graph[currentNode].neighbour) {
// If the neighbour node has not yet been visited
if (!graph[next].visited) {
// Then visit it
graph[next].visited = true;
// And check following elements next time
check.push(next);
}
}
}
return result;
}
int main()
{
// Get the filename from the user
std::cout << "Enter Filename for file with words:\n";
std::string filename{};
//std::cin >> filename;
// Open the file
//std::ifstream textFileStream(filename);
// If the file could be opened . . .
if (textFileStream) {
// Create an empty graph
UndirectedGraph undirectedGraph{};
// Read the complete file into the graph
textFileStream >> undirectedGraph;
Node startWord{}, targetWord{};
std::cout << "Enter start word and enter target word\n"; // teach --> learn
std::cin >> startWord >> targetWord;
// Add the 2 words at the and of our graph
undirectedGraph.graph.push_back(startWord);
undirectedGraph.graph.push_back(targetWord);
// Build adjacency graph, including the just added words
undirectedGraph.build();
// For debug purposes: Show the graph
std::cout << undirectedGraph;
std::cout << "\n\nMorph possible? --> " << std::boolalpha << undirectedGraph.checkForPath() << '\n';
}
else {
// File could not be found or opened
std::cerr << "Error: Could not open file : " << filename;
}
return 0;
}
Please note: Although I have implemented asking for a file name, I do not use it in this example. I read from a istringstream. You need to delete the istringstream later and comment in the existing statements.
Reagarding the requirements from the instructor: I did not use any STL/Library/Boost searching algorithm. (What for? In this example?) But I use of course other C++ STL container. I will not reinvent the wheel and come up with a new "vector" or queue. And I will definitely not use "new" or C-Style arrays or pointer arithmetic.
Have fun!
And to all others: Sorry: I could not resist to write the code . . .

How to implement Breadth First Search?

I was trying to implement BFS in C++, the function should work like this: takes a graph & a vertex as parameters, then create two vectors, one of these is used to store the vertices adjacent with the parameter vertex, then check those vertices, if one of them is unvisited, then get it's adjacencies and add them to vector, after that the vertex should be marked as VISITED and printed; I wrote this code but it did output anything. NOTE: I ensured that the other functions & data structures like the graph are working, the problem is in the BFS function, I hope some of you can help me to fix it.
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
enum Mark {VISITED, UNVISITED};
struct Vertex {
char name;
Mark mark;
};
struct Edge{
Vertex v1;
Vertex v2;
Edge(Vertex vertex1, Vertex vertex2): v1(vertex1), v2(vertex2){};
};
struct Graph{
vector<Vertex>vertices;
vector<Edge>edges;
vector<pair<Vertex, Edge>> adjacent(char u){
vector<pair<Vertex, Edge>>res;
for(Edge e : edges){
if(e.v1.name == u){
res.push_back(make_pair(e.v2, e));
}else if(e.v2.name == u){
res.push_back(make_pair(e.v1, e));
}
}
return res;
}
vector<Vertex> getAdj(char u){
vector<Vertex>result;
for(Edge e: edges){
if(e.v1.name == u){
result.push_back(e.v2);
}else if(e.v2.name == u){
result.push_back(e.v1);
}
}
return result;
}
};
void BFS(Graph g, Vertex u){
vector<Vertex>vec = g.getAdj(u.name);
for(Vertex v : vec){
if(v.mark == UNVISITED){
vector<Vertex>q = g.getAdj(v.name);
for(int i=0; i<q.size(); i++){
vec.push_back(q[i]);
}
v.mark = VISITED;
vec.pop_back();
cout << v.name << " ";
}
}
}
int main(int argc, const char * argv[]) {
Graph g;
Vertex v1, v2, v3, v4, v5, v6;
v1.name = 'A';
v2.name = 'B';
v3.name = 'C';
v4.name = 'D';
v5.name = 'E';
v6.name = 'Z';
g.edges.push_back(Edge(v1, v2));
g.edges.push_back(Edge(v1, v3));
g.edges.push_back(Edge(v2, v3));
g.edges.push_back(Edge(v2, v4));
g.edges.push_back(Edge(v3, v4));
g.edges.push_back(Edge(v3, v5));
g.edges.push_back(Edge(v4, v5));
g.edges.push_back(Edge(v4, v6));
g.edges.push_back(Edge(v5, v6));
BFS(g, v1);
cout << endl;
}
You're not fully initialising your Vertexs. You give them names, but not the initial mark tag.
You can add this in your main:
v1.mark = UNVISITED;
v2.mark = UNVISITED;
v3.mark = UNVISITED;
v4.mark = UNVISITED;
v5.mark = UNVISITED;
v6.mark = UNVISITED;
You can also do something like this (might be better to replace with enum class):
enum Mark {VISITED, UNVISITED};
struct Vertex {
char name;
Mark mark = UNVISITED;
};
(I would also create a constructor to initialise the names to make sure you're not forgetting to do that).
UPDATE:
There are several problems with your BFS:
- Since you're adding elements to vec inside your inner loop, the outer loop isn't working properly (I think it has to do with iterators and happens because you change the size of the vector all the time). I managed to solve it using a regular for loop.
even if you managed to use a ranged for-loop, you're going from "left to right" in the vector. And you're using pop_back() which removes from the end of a vector (so at the rightmost of the vector). I don't think this is what you want.
You have the right idea by marking eacher Vertex as visited and not visited. But you're only updating the Vertexs in vec which are in the BFS function. But they are only copies from the getADj method, which means each time you add the new neighbors to your vec, anytime there are vertices you already visited, that value is not known. Effectively you're adding a new Vertex which is never visited.
There might be other issues, but already with those ones you should have plenty on your hands!
I'd recommend you use a debugger and/or a tool like 'valgrind', to help find problems in your code.

delete the key if the vector associated with it in a multimap is empty

Eventually if I am trying to delete all the elements of a vector associated with a key, I am encountering a segmentation fault. My intended output is new b new c new d new a, but i am getting new b new c new d segmentation fault.
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
int main ()
{
map<char,vector<char> > mmap; //multimap
char mychar[] = { 'b','c', 'd'};
vector<char> vec (mychar,mychar+3);
vector<char> newvec;
mmap.insert (pair<char,vector<char> >('a',vec)); //insert to multimap
mmap.insert (pair<char,vector<char> >('b',vector<char>()));
mmap.insert (pair<char,vector<char> >('c',vector<char>()));
mmap.insert (pair<char,vector<char> >('d',vector<char>()));
vector<char>::iterator veciter;
map<char,vector<char> >::iterator mapiter;
for(int i=0;i<6;i++)
{
for ( mapiter = mmap.begin(); mapiter != mmap.end(); ++mapiter)
{
//if elements associated with vector of a key are empty the store the key in a new vector
if(mapiter->second.empty())
{
newvec.push_back (mapiter->first);
mmap.erase(mapiter);
}
else
{
for (veciter = mapiter->second.begin(); veciter != mapiter->second.end(); ++veciter)
{
//if an element of a vector of key is found in new vector, erase the element
if (find(newvec.begin(), newvec.end(), *veciter)!=newvec.end())
{
mapiter->second.erase(veciter);
}
}
}
// to display values of new vector
for (unsigned i=0; i<newvec.size(); ++i)
cout << "new " << newvec[i]<<' ';
cout << '\n';
}
}
return 0;
}
When you pass an iterator to a container's erase function, that iterator becomes invalidated. You need to account for that. Assuming, for some reason, that neither std::remove nor std::remove_if will work for your situation, the standard idiom goes like this:
for (it = container.begin(); it != container.end(); /* no increment here */)
{
if (should_be_removed(*it))
{
// possibly other operations involving the element we are about to remove
it = container.erase(it);
}
else
{
// possibly other operations involving the element we chose not to remove
++it;
}
}
When we erase an element, we capture the return value of the erase operation, which is the next iterator. Otherwise, we increment. Note the space where I left room for other possible operations. If there are no other operations, you should be able to just use std::remove or std::remove_if, combined with the container's range erase function (the one that takes two iterators).

Graph implementation C++

I was wondering about a quick to write implementation of a graph in c++. I need the data structure to be easy to manipulate and use graph algorithms(such as BFS,DFS, Kruskal, Dijkstra...).
I need this implementation for an algorithms Olympiad, so the easier to write the data structure the better.
Can you suggest such DS(main structs or classes and what will be in them). I know that an Adjacency list and Adjacency matrix are the main possibilities, but I mean a more detailed code sample.
For example I thought about this DS last time I had to implement a graph for DFS:
struct Edge {
int start;
int end;
struct Edge* nextEdge;
}
and then used a array of size n containing in its i'th place the Edge List(struct Edge) representing the edges starting in the i'th node.
but when trying to DFS on this graph I had to write a 50 line code with about 10 while loops.
What 'good' implementations are there?
Below is a implementation of Graph Data Structure in C++ as Adjacency List.
I have used STL vector for representation of vertices and STL pair for denoting edge and destination vertex.
#include <iostream>
#include <vector>
#include <map>
#include <string>
using namespace std;
struct vertex {
typedef pair<int, vertex*> ve;
vector<ve> adj; //cost of edge, destination vertex
string name;
vertex(string s) : name(s) {}
};
class graph
{
public:
typedef map<string, vertex *> vmap;
vmap work;
void addvertex(const string&);
void addedge(const string& from, const string& to, double cost);
};
void graph::addvertex(const string &name)
{
vmap::iterator itr = work.find(name);
if (itr == work.end())
{
vertex *v;
v = new vertex(name);
work[name] = v;
return;
}
cout << "\nVertex already exists!";
}
void graph::addedge(const string& from, const string& to, double cost)
{
vertex *f = (work.find(from)->second);
vertex *t = (work.find(to)->second);
pair<int, vertex *> edge = make_pair(cost, t);
f->adj.push_back(edge);
}
It really depends on what algorithms you need to implement, there is no silver bullet (and that's shouldn't be a surprise... the general rule about programming is that there's no general rule ;-) ).
I often end up representing directed multigraphs using node/edge structures with pointers... more specifically:
struct Node
{
... payload ...
Link *first_in, *last_in, *first_out, *last_out;
};
struct Link
{
... payload ...
Node *from, *to;
Link *prev_same_from, *next_same_from,
*prev_same_to, *next_same_to;
};
In other words each node has a doubly-linked list of incoming links and a doubly-linked list of outgoing links. Each link knows from and to nodes and is at the same time in two different doubly-linked lists: the list of all links coming out from the same from node and the list of all links arriving at the same to node.
The pointers prev_same_from and next_same_from are used when following the chain of all the links coming out from the same node; the pointers prev_same_to and next_same_to are instead used when managing the chain of all the links pointing to the same node.
It's a lot of pointer twiddling (so unless you love pointers just forget about this) but query and update operations are efficient; for example adding a node or a link is O(1), removing a link is O(1) and removing a node x is O(deg(x)).
Of course depending on the problem, payload size, graph size, graph density this approach can be way overkilling or too much demanding for memory (in addition to payload you've 4 pointers per node and 6 pointers per link).
A similar structure full implementation can be found here.
This question is ancient but for some reason I can't seem to get it out of my mind.
While all of the solutions do provide an implementation of graphs, they are also all very verbose. They are simply not elegant.
Instead of inventing your own graph class all you really need is a way to tell that one point is connected to another -- for that, std::map and std::unordered_map work perfectly fine. Simply, define a graph as a map between nodes and lists of edges. If you don't need extra data on the edge, a list of end nodes will do just fine.
Thus a succinct graph in C++, could be implemented like so:
using graph = std::map<int, std::vector<int>>;
Or, if you need additional data,
struct edge {
int nodes[2];
float cost; // add more if you need it
};
using graph = std::map<int, std::vector<edge>>;
Now your graph structure will plug nicely into the rest of the language and you don't have to remember any new clunky interface -- the old clunky interface will do just fine.
No benchmarks, but I have a feeling this will also outperform the other suggestions here.
NB: the ints are not indices -- they are identifiers.
The most common representations are probably these two:
Adjacency list
Adjacency matrix
Of these two the adjacency matrix is the simplest, as long as you don't mind having a (possibly huge) n * n array, where n is the number of vertices. Depending on the base type of the array, you can even store edge weights for use in e.g. shortest path discovery algorithms.
I prefer using an adjacency list of Indices ( not pointers )
typedef std::vector< Vertex > Vertices;
typedef std::set <int> Neighbours;
struct Vertex {
private:
int data;
public:
Neighbours neighbours;
Vertex( int d ): data(d) {}
Vertex( ): data(-1) {}
bool operator<( const Vertex& ref ) const {
return ( ref.data < data );
}
bool operator==( const Vertex& ref ) const {
return ( ref.data == data );
}
};
class Graph
{
private :
Vertices vertices;
}
void Graph::addEdgeIndices ( int index1, int index2 ) {
vertices[ index1 ].neighbours.insert( index2 );
}
Vertices::iterator Graph::findVertexIndex( int val, bool& res )
{
std::vector<Vertex>::iterator it;
Vertex v(val);
it = std::find( vertices.begin(), vertices.end(), v );
if (it != vertices.end()){
res = true;
return it;
} else {
res = false;
return vertices.end();
}
}
void Graph::addEdge ( int n1, int n2 ) {
bool foundNet1 = false, foundNet2 = false;
Vertices::iterator vit1 = findVertexIndex( n1, foundNet1 );
int node1Index = -1, node2Index = -1;
if ( !foundNet1 ) {
Vertex v1( n1 );
vertices.push_back( v1 );
node1Index = vertices.size() - 1;
} else {
node1Index = vit1 - vertices.begin();
}
Vertices::iterator vit2 = findVertexIndex( n2, foundNet2);
if ( !foundNet2 ) {
Vertex v2( n2 );
vertices.push_back( v2 );
node2Index = vertices.size() - 1;
} else {
node2Index = vit2 - vertices.begin();
}
assert( ( node1Index > -1 ) && ( node1Index < vertices.size()));
assert( ( node2Index > -1 ) && ( node2Index < vertices.size()));
addEdgeIndices( node1Index, node2Index );
}
There can be an even simpler representation assuming that one has to only test graph algorithms not use them(graph) else where. This can be as a map from vertices to their adjacency lists as shown below :-
#include<bits/stdc++.h>
using namespace std;
/* implement the graph as a map from the integer index as a key to the adjacency list
* of the graph implemented as a vector being the value of each individual key. The
* program will be given a matrix of numbers, the first element of each row will
* represent the head of the adjacency list and the rest of the elements will be the
* list of that element in the graph.
*/
typedef map<int, vector<int> > graphType;
int main(){
graphType graph;
int vertices = 0;
cout << "Please enter the number of vertices in the graph :- " << endl;
cin >> vertices;
if(vertices <= 0){
cout << "The number of vertices in the graph can't be less than or equal to 0." << endl;
exit(0);
}
cout << "Please enter the elements of the graph, as an adjacency list, one row after another. " << endl;
for(int i = 0; i <= vertices; i++){
vector<int> adjList; //the vector corresponding to the adjacency list of each vertex
int key = -1, listValue = -1;
string listString;
getline(cin, listString);
if(i != 0){
istringstream iss(listString);
iss >> key;
iss >> listValue;
if(listValue != -1){
adjList.push_back(listValue);
for(; iss >> listValue; ){
adjList.push_back(listValue);
}
graph.insert(graphType::value_type(key, adjList));
}
else
graph.insert(graphType::value_type(key, adjList));
}
}
//print the elements of the graph
cout << "The graph that you entered :- " << endl;
for(graphType::const_iterator iterator = graph.begin(); iterator != graph.end(); ++iterator){
cout << "Key : " << iterator->first << ", values : ";
vector<int>::const_iterator vectBegIter = iterator->second.begin();
vector<int>::const_iterator vectEndIter = iterator->second.end();
for(; vectBegIter != vectEndIter; ++vectBegIter){
cout << *(vectBegIter) << ", ";
}
cout << endl;
}
}
Here is a basic implementation of a graph.
Note: I use vertex which is chained to next vertex. And each vertex has a list pointing to adjacent nodes.
#include <iostream>
using namespace std;
// 1 ->2
// 1->4
// 2 ->3
// 4->3
// 4 -> 5
// Adjacency list
// 1->2->3-null
// 2->3->null
//4->5->null;
// Structure of a vertex
struct vertex {
int i;
struct node *list;
struct vertex *next;
};
typedef struct vertex * VPTR;
// Struct of adjacency list
struct node {
struct vertex * n;
struct node *next;
};
typedef struct node * NODEPTR;
class Graph {
public:
// list of nodes chained together
VPTR V;
Graph() {
V = NULL;
}
void addEdge(int, int);
VPTR addVertex(int);
VPTR existVertex(int i);
void listVertex();
};
// If vertex exist, it returns its pointer else returns NULL
VPTR Graph::existVertex(int i) {
VPTR temp = V;
while(temp != NULL) {
if(temp->i == i) {
return temp;
}
temp = temp->next;
}
return NULL;
}
// Add a new vertex to the end of the vertex list
VPTR Graph::addVertex(int i) {
VPTR temp = new(struct vertex);
temp->list = NULL;
temp->i = i;
temp->next = NULL;
VPTR *curr = &V;
while(*curr) {
curr = &(*curr)->next;
}
*curr = temp;
return temp;
}
// Add a node from vertex i to j.
// first check if i and j exists. If not first add the vertex
// and then add entry of j into adjacency list of i
void Graph::addEdge(int i, int j) {
VPTR v_i = existVertex(i);
VPTR v_j = existVertex(j);
if(v_i == NULL) {
v_i = addVertex(i);
}
if(v_j == NULL) {
v_j = addVertex(j);
}
NODEPTR *temp = &(v_i->list);
while(*temp) {
temp = &(*temp)->next;
}
*temp = new(struct node);
(*temp)->n = v_j;
(*temp)->next = NULL;
}
// List all the vertex.
void Graph::listVertex() {
VPTR temp = V;
while(temp) {
cout <<temp->i <<" ";
temp = temp->next;
}
cout <<"\n";
}
// Client program
int main() {
Graph G;
G.addEdge(1, 2);
G.listVertex();
}
With the above code, you can expand to do DFS/BFS etc.