Indie_Cube
Indie_Cube

Reputation: 47

Qsort() doesn't work on a struct

I'm currently implementing some algorithm on graphs and I use a struct to keep the information about every edge in the graph: its source vertex, its destination vertex and its weight.

I have my struct declared as following:

typedef struct edge {
   int data[3]; //src, dest, weight
} edge_t, *edge_p; 

Then I create a variable pointer and allocate memory for n structs, where n is a number of edges in a graph:

edge_p localEdges = (edge_p)malloc(n*sizeof(edge_t));

Then I fill up struct localEdges with the values of another struct allEdges of the same type:

for (int i = 0; i < num_edges; i++) {
    localEdges[i].data[0] = allEdges[i].data[0];
    localEdges[i].data[1] = allEdges[i].data[1];
    localEdges[i].data[2] = allEdges[i].data[2];
}

And then I need to sort the contents of localEdges by ascending order of data[2] field (by ascending edge weight). My compare function is this:

int myComp (const void *a, const void *b)
{
    const edge_t * ptr_a = (const edge_t *)a;
    const edge_t * ptr_b = (const edge_t *)b;
    return ptr_b->data[2] < ptr_a->data[2];
}

And the call for the function is the following:

qsort(localEdges, n, sizeof(edge_t), myComp);

However, that doesn't work. The processed localEdges array has some data placed wrongly. For example:

localEdges[0].data[2] = 1
localEdges[1].data[2] = 2
localEdges[2].data[2] = 1
localEdges[3].data[2] = 2
localEdges[4].data[2] = 3
localEdges[5].data[2] = 3
localEdges[6].data[2] = 4

When it should be:

localEdges[0].data[2] = 1
localEdges[1].data[2] = 1
localEdges[2].data[2] = 2
localEdges[3].data[2] = 2
localEdges[4].data[2] = 3
localEdges[5].data[2] = 3
localEdges[6].data[2] = 4

I guess there is something with pointers, but I'm not quite confident with them.

Any suggestions? I'll Appreciate your help.

Upvotes: 1

Views: 313

Answers (3)

Yury Schkatula
Yury Schkatula

Reputation: 5369

It seems your comparer function is incorrectly implemented. It never returns negative result, just bool value (comparison operation) converted to 0 and 1. Could you try something like that:

int myComp (const void *a, const void *b)
{
    const edge_t * ptr_a = (const edge_t *)a;
    const edge_t * ptr_b = (const edge_t *)b;
    return (ptr_b->data[2] < ptr_a->data[2]) ? -1 : 
        (ptr_b->data[2] > ptr_a->data[2]) ? 1 : 0;
}

Upvotes: 0

user2371524
user2371524

Reputation:

In short: Your comparison function is wrong. Citing a qsort() manual:

The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second. If two members compare as equal, their order in the sorted array is undefined.

So, if you're returning 0, the two elements are considered equal.

return ptr_b->data[2] < ptr_a->data[2];

This line does return 0 if the value in *ptr_b is greater than that in *ptr_a.

The correct way to do it is as follows:

int myComp (const void *a, const void *b)
{
    const edge_t * ptr_a = (const edge_t *)a;
    const edge_t * ptr_b = (const edge_t *)b;
    if (ptr_a->data[2] < ptr_b->data[2]) return -1;
    if (ptr_a->data[2] > ptr_b->data[2]) return 1;
    return 0;
}

Upvotes: 3

Vlad from Moscow
Vlad from Moscow

Reputation: 310980

From the C Standard (7.22.5.2 The qsort function)

3 The contents of the array are sorted into ascending order according to a comparison function pointed to by compar, which is called with two arguments that point to the objects being compared. The function shall return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.

Thus define the function for example the following way

int myComp (const void *a, const void *b)
{
    const edge_t * ptr_a = (const edge_t *)a;
    const edge_t * ptr_b = (const edge_t *)b;

    return ( ptr_b->data[2] < ptr_a->data[2] ) - ( ptr_a->data[2] < ptr_b->data[2] );
}

Upvotes: 2

Related Questions