0

I am learning some graph algorithms and decided to implement a graph as an adjacency list. I am not very comfortable with C++ and I think I am making some syntactic mistakes on how to go about creating a graph represented by a map, with vertex as the key and vector<vertex> as the value. All of the following is in one file, with this at the top:

#include <iostream>
#include <random>
#include <chrono>
#include <assert.h>
#include <algorithm>
#include <unordered_map>

using namespace std;

I realize it might be cleaner to use pointers, but I've committed to this approach for now: I have a vertex object:

struct vertex
{
    int number; // unique identifier - can be 0
    int parent;
    int distance_from_root;
    char color; // one of 'w', 'b', 'g'

    bool const operator==(const int num) const // define operator for handling map with this as key
    {
        return num == number;
    }
};

Where I have added the operator because I guess C++ needs some way to compare vertices when they're in a map, and so doing map[3] should find the key that has 3 as key.number I assume. I only did this based on some SO question. Then this is my attempt to generate a graph (an adjacency list):

unordered_map<vertex, <vector<vertex>>, vertexHasher> generate_random_graph(int nv) 
{ // take as input the number of vertices in the map we're creating

    // initialize vertex objects for use in map construction later
    vector<vertex> all_vertices; 
    for (int i = 0; i < nv; ++i){ 
        vertex v; 
        v.number = i; 
        all_vertices.push_back(v);
    }
    
    // initially didn't use vertexHasher but some SO question said we need it - not sure why
    unordered_map<vertex, <vector<vertex>>, vertexHasher> adj_list; 
    for (int i = 0; i < nv; ++i){
        vector<vertex> cur_v_conns; // vertices we want our new vertex to connect to
        // create vector of vertices we want this one vertex to connect to
        int n_conns_for_cur_v = rand_int(1, 5);
        for (int j = 0; j < n_conns_for_cur_v; ++j){ 
            vertex rand_v = all_vertices[rand_int(0, all_vertices.size()-1)];
            bool exists = false; 
            for (auto conn: cur_v_conns){ // ensure we don't connect our next v to another one twice
                if (conn.number == rand_v.number) exists = true; 
            } if (!exists) cur_v_conns.push_back(rand_v); // add rand_vertex as a connection to our current it it doesn't exist
        }
        adj_list.insert({ all_vertices[i]: cur_v_conns }); // add cur_v:c cur_v_conns to map
    }
    return adj_list;
}

In case it is relevant, here is vertexHasher (not sure why this is there, but some SO question said we needed a custom hash function?)

struct vertexHasher
{
    size_t operator()(const vertex &v) const
    {
        using std::hash;
        using std::size_t;
        using std::string;

        return ((hash<string>()(v.number) ^ (hash<string>()(v.color) << 1)) >> 1) ^ (hash<int>()(v.distance_from_root) << 1);
    }
};

I'm sure there are several little mistakes and I'd appreciate some elaboration on how one might approach a problem like this so I can learn some stuff about C++, but if you're looking for a concrete problem then I'm getting "expected a type identifier" error on the line of generate_random_graph. Thank you! And do you think this is a reasonable graph interpretation, by the by, or is an implementation using pointers and Edge objects strictly better?

-- EDIT -- Here is the complete compile error (but I suspect there are more structural problems than just these superficial errors)

/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:101:23: error: expected expression
unordered_map<vertex, <vector<vertex>>, vertexHasher> generate_random_graph(int nv)
                      ^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:101:38: error: expected '(' for function-style cast or type construction
unordered_map<vertex, <vector<vertex>>, vertexHasher> generate_random_graph(int nv)
                       ~~~~~~~~~~~~~~^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:101:39: error: expected unqualified-id
unordered_map<vertex, <vector<vertex>>, vertexHasher> generate_random_graph(int nv)
                                      ^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:129:40: error: expected expression
void print_graph(unordered_map<vertex, <vector<vertex>>, vertexHasher> adj_list){
                                       ^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:129:55: error: expected '(' for function-style cast or type construction
void print_graph(unordered_map<vertex, <vector<vertex>>, vertexHasher> adj_list){
                                        ~~~~~~~~~~~~~~^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:129:40: error: expected expression
void print_graph(unordered_map<vertex, <vector<vertex>>, vertexHasher> adj_list){
                                       ^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:129:55: error: expected '(' for function-style cast or type construction
void print_graph(unordered_map<vertex, <vector<vertex>>, vertexHasher> adj_list){
                                        ~~~~~~~~~~~~~~^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:129:70: error: expected ')'
void print_graph(unordered_map<vertex, <vector<vertex>>, vertexHasher> adj_list){
                                                                     ^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:129:17: note: to match this '('
void print_graph(unordered_map<vertex, <vector<vertex>>, vertexHasher> adj_list){
                ^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:131:25: error: use of undeclared identifier 'adj_list'
    for (int i = 0; i < adj_list.size(); ++i){ // print each el in adj_list
                        ^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:134:29: error: use of undeclared identifier 'adj_list'
        for (int j = 0; j < adj_list[i].size(); ++j){
                            ^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:135:22: error: use of undeclared identifier 'adj_list'
            if (j == adj_list[i].size()-1){
                     ^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:136:25: error: use of undeclared identifier 'adj_list'
                cout << adj_list[i][j].number << endl;
                        ^
/Users/tanishqkumar/Desktop/cs124/algs/helpers.hh:139:25: error: use of undeclared identifier 'adj_list'
                cout << adj_list[i][j].number << ", ";
                        ^
/Users/tanishqkumar/Desktop/cs124/algs/graphs.cc:11:31: error: expected expression
typedef unordered_map<vertex, <vector<vertex>>, vertexHasher> adj_list_type; 
                              ^
/Users/tanishqkumar/Desktop/cs124/algs/graphs.cc:11:46: error: expected '(' for function-style cast or type construction
typedef unordered_map<vertex, <vector<vertex>>, vertexHasher> adj_list_type; 
                               ~~~~~~~~~~~~~~^
/Users/tanishqkumar/Desktop/cs124/algs/graphs.cc:11:47: error: expected unqualified-id
typedef unordered_map<vertex, <vector<vertex>>, vertexHasher> adj_list_type;
Tanishq Kumar
  • 263
  • 1
  • 13
  • You need to be careful interchanging `map` and `unordered_map`. They are very different containers and have different requirements for their keys. Please provide the complete compile error you're receiving. It would also be a good idea to fix this up into a [mcve] so we can see the full picture as it may be relevant to the error (missing includes, using namespace std, etc.). – Retired Ninja Dec 29 '20 at 06:59
  • Try removing the extra `<>` around `>` everywhere it occurs. – Retired Ninja Dec 29 '20 at 07:09
  • I did and that seems to have made things better but some problems persist - see my comment on Ami's answer. – Tanishq Kumar Dec 29 '20 at 07:21

1 Answers1

0

This compiles and does something. Perhaps it will help you proceed. I've added comments on the things I had to change in order to make it work.

#include <iostream>
#include <random>
#include <chrono>
#include <assert.h>
#include <algorithm>
#include <unordered_map>

using namespace std;

std::random_device rd;
std::mt19937 random_generator(rd());

int rand_int(int low, int high)
{
    return std::uniform_int_distribution<int>(low, high)(random_generator);
}

struct vertex
{
    int number;
    int parent;
    int distance_from_root;
    char color; // one of 'w', 'b', 'g'

    // This needs to compare vertex with vertex, not vertex with int
    bool const operator==(const vertex& rhs) const
    {
        return number == rhs.number;
    }
};

std::ostream &operator<<(std::ostream &out, const vertex& v)
{
    out << "[ number: " << v.number << " ]";
    return out;
}

struct vertexHasher
{
    size_t operator()(const vertex &v) const
    {
        // The template parameter needs to match the type. 
        return ((hash<int>()(v.number) ^ (hash<char>()(v.color) << 1)) >> 1) ^ (hash<int>()(v.distance_from_root) << 1);
    }
};

unordered_map<vertex, vector<vertex>, vertexHasher> generate_random_graph(int nv)
{
    vector<vertex> all_vertices;
    for (int i = 0; i < nv; ++i)
    {
        vertex v;
        v.number = i;
        all_vertices.push_back(v);
    }

    unordered_map<vertex, vector<vertex>, vertexHasher> adj_list;
    for (int i = 0; i < nv; ++i)
    {
        vector<vertex> cur_v_conns;
        int n_conns_for_cur_v = rand_int(1, 5);
        for (int j = 0; j < n_conns_for_cur_v; ++j)
        {
            vertex rand_v = all_vertices[rand_int(0, all_vertices.size() - 1)];
            bool exists = false;
            // Use const reference since the object doesn't need to be modified or copied
            for (const auto& conn : cur_v_conns)
            { 
                if (conn.number == rand_v.number)
                {
                    exists = true;
                    // break since there's no reason to continue once a duplicate is found
                    break;
                }
            } 
            if (!exists)
            {
                cur_v_conns.push_back(rand_v);
            }
        }
        // Use comma, not colon
        adj_list.insert({ all_vertices[i], cur_v_conns });
    }
    return adj_list;
}

int main()
{
    auto graph = generate_random_graph(10);
    for (const auto &kvp : graph)
    {
        std::cout << "key: " << kvp.first << "\n";
        std::string delim = "values: ";
        for (const auto &v : kvp.second)
        {
            std::cout << delim << v;
            delim = ", ";
        }
        std::cout << "\n\n";
    }
}
Retired Ninja
  • 4,785
  • 3
  • 25
  • 35