2

Environment:
- Win7 pro x64
- VS2010
- C++
- Empty Project

Goal: Implementation of Dijkstra's shortest path algo using a priority queue.

Problem: When the program runs it gets a Debug assertion failed, Expression: invalid heap error. If the user inputs the source vertex as 1, everything works fine. The assertion only occurs when the source vertex is other than 1. Also, if the assertions are ignored, the code finishes eventually and outputs the proper paths through the graph. I am guessing the error has something to do with altering the data that pointers in the priority queue point to, but if this is the case, I don't understand why using 1 as the source allows the code to complete successfully.

Thanks for any and all help!

header:

#ifndef _GRAPH_H_
#define _GRAPH_H_
#include <map>
#include <queue>
#include <vector>
#include <fstream>

using namespace std;

class Graph
{

public:

    struct Vertex
    {
        int name; // V number
        double dv; // distance
        Vertex* pv; // previous V* from this V
        map<Vertex*, double> neighbors; // map of all neighbors/distances connected to vert
    };

    vector<Vertex*> verts; // vector of all V*

    void dijkstra(ifstream& stream, int start_vert); // create graph & find shortest paths
    void printPath(Vertex* v); // echo path

    class CompareVert // overloaded compare operator for priorty queue data struct, sort queue so V with smallest dist on top
    {
    public:
        bool operator()(const Vertex* v1, const Vertex* v2) const
        {
            return v1->dv > v2->dv;
        }
    };
};

#endif

implementation:

#include "Graph.h"
#include <iostream>
#include <queue>
#include <limits> // used for numeric_limits<double>::infinity()
#include <vector>

using namespace std;

int path_length = 0;

void Graph::printPath(Vertex* v) // print shortest paths
{
    if (v->pv != NULL)
    {
        printPath(v->pv);
        cout << " -> ";
    }
    cout << v->name;
}

void Graph::dijkstra(ifstream& stream, int start_vert) // create graph & get shortest path
{
    /////////////////////////////////////////////
    /////////////// create graph ////////////////
    /////////////////////////////////////////////

    int total_edges;
    priority_queue<Vertex*, vector<Vertex*>, CompareVert> q;
    double infinity = numeric_limits<double>::infinity();
    int source;
    int dest;
    double dist;
    stream >> total_edges;
    for (int i=0;i<total_edges;i++)
    {
        stream >> source;
        stream >> dest;
        stream >> dist;
        bool source_exists = false;
        bool dest_exists = false;
        Vertex* _source;
        Vertex* _dest;

        for (int i=0;i<verts.size();i++)
        {
            if (verts.at(i)->name == source) // vertex already exists, set to V
            {
                _source = verts.at(i);
                source_exists = true;
                break;
            }
        }

        for (int i=0;i<verts.size();i++)
        {
            if (verts.at(i)->name == dest) // vertex already exists, set to V
            {
                _dest = verts.at(i);
                dest_exists = true;
                break;
            }
        }

        if (!source_exists) // create vert
        {
            _source = new Vertex;
            _source->name = source;
            _source->dv = infinity;
            _source->pv = NULL;
            verts.push_back(_source);
        }

        if (!dest_exists) // create vert
        {
            _dest = new Vertex;
            _dest->name = dest;
            _dest->dv = infinity;
            _dest->pv = NULL;
            verts.push_back(_dest);
        }
        _source->neighbors.insert(pair<Vertex*, double>(_dest, dist)); // populate V's adjacency map
    }

    for (int i=0;i<verts.size();i++)
    {
        if (verts.at(i)->name == start_vert) // set source
        {
            verts.at(i)->dv = 0;
        }       
        q.push(verts.at(i)); // push all vertices to priority queue
    }

    /////////////////////////////////////////////
    ////////////////  find paths  ///////////////
    /////////////////////////////////////////////

    vector<int> displayed;
    bool print; // flag to call printPath
    while (!q.empty())
    {
        map<Vertex*, double>::iterator it;
        Vertex* temp = q.top(); // get V with smallest dist
        print = true;
        for (it = temp->neighbors.begin(); it!=temp->neighbors.end();++it)
        {
            if ((temp->dv + it->second) < it->first->dv)
            {
                print = false;
                it->first->dv = (temp->dv + it->second);
                it->first->pv = temp;
                q.push(it->first);
            }
        }

        for (int i=0;i<displayed.size();i++) // if end V of path has already been printed, do not print
        {
            if (displayed.at(i) == temp->name)
                print = false;
        }

        if (print == true)
        {
            printPath(temp);
            path_length = temp->dv;
            cout << " total distance = " << path_length <<endl << endl;
            displayed.push_back(temp->name);
        }

        path_length = 0;
        q.pop();
    }
}

driver:

#include "Graph.h"
#include <stdio.h>
#include <iostream>
#include <string>
#include <fstream>
#include <list>

using namespace std;

string fname;
int vname;
string line;

int main(void)
{
    cout << "Please enter the file to read in a graph (graph.txt): ";
    cin >> fname;
    cout << "Please choose a starting vertex (1 is a good choice): ";
    cin >> vname;
    cout << endl;

    ifstream my_stream (fname);
    Graph my_graph;
    my_graph.dijkstra(my_stream, vname);
    my_stream.close();
}

graph.txt:

12
1 2 2
1 4 1
2 4 3
2 5 10
3 1 4
3 6 5
4 3 2
4 5 2
4 6 8
4 7 4
5 7 6
7 6 1
4
  • 1
    Have you tried stepping through your code in a debugger? Commented May 2, 2011 at 0:53
  • I have. It seems it is the push_heap call that causes it. Commented May 2, 2011 at 0:54
  • 1
    Do you mean push_back? Commented May 2, 2011 at 0:56
  • @Oli, actually it is the push_back, but more precisely when I stepped into the code it was during the push_heap call that priority queue uses from the 'algorithm' include. Commented May 2, 2011 at 1:08

3 Answers 3

1

You should pop the top element from the priority_queue right after your call to q.top(). Instead you do q.pop() after pushing a new element into the queue with q.push(it->first);

That seems to me to be not what you want because you could now potentially be popping an element different from what you thought was the top element.

Sign up to request clarification or add additional context in comments.

3 Comments

I tried changing the code to q.pop() after assigning q.top() to temp. Unfortunately the error still occurs, and to make matters worse it happens regardless which vertex is chosen as the source, 1 included.
Putting the pop() after top() is the right thing to do algorithmically, even thought it doesn't fix your problem. Maybe you have some un-initialized stuff. I would try to comment out some stuff in your priority_queue and see what happens. First thing would be to comment out the map<> in the Vertex structure.
Your struct Vertex is just has a bunch of data members, which is why I immediately suspect that something is not being initialized properly.
1

Its possible you're pushing Vertex instances with infinite distances. Comparing two infinite distances would yield none of them being smaller than the other, therefore invalidating your comparator.

If this is strictly a learning exercise and you don't really require double distances, I suggest you use integer distances and use a "large" number in place of infinite.

Using N bit integers it is a good idea to use 2^(N-3) as your infinite value, because adding them would yield 2^(N-2) which is still representable by a signed N bit integer (not so 2^(N-1)), and it is a larger value than a simple infinite, i.e. infinite + infinite > infinite, which keeps your ordering sane.

Comments

0

You should avoid modifying elements that are currently in the priority queue, at least not in a way that modify their priority. The priority of the vertices is given by CompareVert, which depends on the value of dv. In the "find path section" you have

 if ((temp->dv + it->second) < it->first->dv)
 {
     print = false;
     it->first->dv = (temp->dv + it->second);   // <-- here is the problem!
     it->first->pv = temp;
     q.push(it->first);
  }

The assignment to dv affect an element currently in the queue. When you call q.push(), the queue realizes it's in an invalid state and complains

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.