HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Shortest path Dijkstra's algorithm

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
algorithmpathdijkstrashortest

Problem

I'm trying to implement Dijkstra's algorithm to find the shortest path from a starting node to the last node of a 250px by 200px raw image file (e.g. starting node = [0][0], ending node = [250][200]). The raw file acts like a topographical map in that each byte represents an elevation and the distance cost from one node to its adjacent node(s) is the difference in elevations of the two nodes plus 1 for the energy to move from one node to its neighbor (movement can only happen horizontally and vertically).

I have followed the pseudocode for the algorithm using a priority queue in C++, but am struggling to debug to see if I have successfully implemented the algorithm correctly as well as implementing the algorithm to accomplish this unique and relatively challenging situation. Also, my goal is to output the shortest path in a raw file and then view it in Photoshop to see if it is being displayed correctly, but have failed completely at this task.

After all that, my questions are:

  • Are there any visible/blaring logical errors in my implementation of solving the above described problem?



  • Could anyone suggest some ideas on how to try to output the shortest path in a raw file or something along similar lines?



I know I've used file read and write operations with the C notation rather than the stream operations of C++ so there's no need of telling me how I can change that.

```
#include
#include
#include
#include

#define INFINITY 99999
#define MAP_SIZE 50000 // 250px by 200px

void dijkstra(int, int, unsigned int []);

// The terminology 'Node' and 'Vertex' are used interchangeably
// throughout the program.
struct Node
{
int index; // index of node in graph
unsigned int distance; // distance from source (only allow positive distances)
};

// A simple class to compare the distances of two Node structures.
class CompareDist
{
public:
bool operator()(Node& n1, Node& n2)
{
if (n1.distance source node
// int size -->

Solution

There's a few things I would suggest here that could make your code more idiomatic c++.

#define INFINITY 99999
#define MAP_SIZE 50000  // 250px by 200px


First off I'd make these const ints:

const unsigned int INFINITY = 99999; //more about this choice of data type later
const int MAP_SIZE = 250 * 200;


This way you now have named variables that have types. This can solve some problems immediately when you get type conversions that you didn't intend or want and it can help if you are debugging your code in a debugger.

The next thing is that I would change INFINITY to be the largest value that can possibly be represented in that type:

#include
const int INFINITY = std::numeric_limits::max();


Now that INFINITY is the largest value that can be stored in an unsigned int you won't ever have x > INFINITY evaluate to true if x is an unsigned int. This can potentially prevent some subtle bugs, later on when you initialize your distance you are using this value and seeing as distance was storing an array of unsigned ints you want infinity to be the same type. (Compile with -Wconversion and you might see warnings from doing comparisons with signed vs unsigned, take those warnings seriously too.)

Your comparison class (I would make it a struct but that's probably a minor point) seems mostly fine, but I would change the comparison to take const references.

struct CompareDist
{
    bool operator()(Node const& n1, Node const& n2)
    {
        return (n1.distance < n2.distance);
    }
};


You also can just return the result directly and skip the if-else block entirely as it's completely redundant.

unsigned char read[MAP_SIZE];
unsigned int original[MAP_SIZE];    //2D array implemented in 1D array
unsigned char path[MAP_SIZE];


For all these I would use the newer std::array classes along with a typedef for convenience.

typedef std::array graph_array_t;
typedef std::array graph_path_array_t;

graph_array_t original;    //2D array implemented in 1D array
graph_path_array_t path;
graph_path_array_t read;


The std::array doesn't have some of the pitfalls of the raw arrays, works better with STL algorithms and doesn't cost you any performance hit.

I would change a few things with the first loop in main:

for (int i = 0; i (read[i]);
}


You'll see the scope of i is now reduced just to that for loop. If you try to reuse the loop variable later you will now get a compiler warning or error. I also removed one of the casts, it's fairly dubious to be making 2 casts in a row, if changing this to one cast breaks the code you really need to comment and explain why. If you are doing that because you need a particular width for your integers you should make a comment about that and explicitly specify the width in bits that you want by using the specific data types like uint8_t uint16_t as these will be guaranteed to work across different platforms. Also I've used the newer c++ cast syntax because it's easier to search for and specifies the exact type of cast you wish to perform.

Given the change of the data type for the graph the function parameters now have to change:

void dijkstra(int source, int size, graph_array_t const& graph)


In that function you have pointers to primitive data types, just store those types by value. You only really want to go to the extra effort of storing pointers to the data if the data is bigger than a pointer representing that data.

bool *visited = new bool [size];                    // array to check which vertices have already been visited
unsigned int *distance = new unsigned int [size];   // table to hold the distances of each vertex from the source vertex


So I'd use a std::vector to store these by value like so:

std::vector visited(size);
std::vector distance(size);

Code Snippets

#define INFINITY 99999
#define MAP_SIZE 50000  // 250px by 200px
const unsigned int INFINITY = 99999; //more about this choice of data type later
const int MAP_SIZE = 250 * 200;
#include<limits>
const int INFINITY = std::numeric_limits<unsigned int>::max();
struct CompareDist
{
    bool operator()(Node const& n1, Node const& n2)
    {
        return (n1.distance < n2.distance);
    }
};
unsigned char read[MAP_SIZE];
unsigned int original[MAP_SIZE];    //2D array implemented in 1D array
unsigned char path[MAP_SIZE];

Context

StackExchange Code Review Q#71189, answer score: 7

Revisions (0)

No revisions yet.