4

So, actually what I need is to keep the index of old array after sorted. So for example if I input [2,4,1,5,7,9,6] then the output is [2,0,1,3,6,4,5]. I already have use qsort and it works very well if there are no duplicate elements.

If there are duplicate elements, sometimes the first duplicate element was put last. For example, if the input is [5,4,6,5,2,1,3], what I want to be output is [5,4,6,1,0,3,2]. So, 5 which have index 0 put before 5 which have index 3. But, using qsort sometimes make the output [5,4,6,1,3,0,2].

Can you help me to fix this? Or should I create my own sorting function? Could you please help me to create it?

Here is my code:

#include <stdlib.h>

int* sortidx(double *X,int n)
{
    int *idx,i,j;

    int cmp(const void *a,const void *b)
    {
        return X[*(int*)a]>=X[*(int*)b]?1:-1;
    }

    idx=(int*)calloc(n,sizeof(int));

    for(i=0;i<n;i++)
    {
        idx[i]=i;
    }

    qsort(idx,n,sizeof(int),cmp);

    return idx;
}
gsamaras
  • 71,951
  • 46
  • 188
  • 305
fahadh4ilyas
  • 450
  • 2
  • 13
  • 2
    You seem to need what is called a "stable" sorting algorithm. Check here https://en.wikipedia.org/wiki/Sorting_algorithm#Stability the meaning. Then choose an algorithm among those described as "stable" and you should be set up. – Yunnosch Aug 13 '17 at 14:26
  • What you're looking for is called a *stable sort*. Googling "stable qsort c" brings up a few hits; I'd suggest you look through those. – NPE Aug 13 '17 at 14:27
  • 1
    btw just noting you're using nested functions which is a GCC extension. – Antti Haapala -- Слава Україні Aug 13 '17 at 15:34

2 Answers2

5

You want one element to be considered greater than another if either its value is greater or if the values are equal and its index is greater. (This is the idea behind a stable sort algorithm.)

In this case, you know the indices of the elements being compared, so you can easily add that to your comparison criterion:

int cmp(const void *a, const void *b)
{
    return X[*(int*)a] > X[*(int*)b] ||
           (X[*(int*)a] == X[*(int*)b] && *(int*)a > *(int*)b)
           ?1:-1;
}

or, possibly more readably and pedantically correct (since it is not documented that a and b are guaranteed to be different):

int cmp(const void *a, const void *b)
{
    int idxa = *(const int*)a, idxb = *(const int*)b;
    if (X[idxa] > X[idxb]) return 1;
    if (X[idxa] < X[idxb]) return -1;
    return idxa - idxb;
}

The use of a nested function which refers to the argument X is a gcc extension, and may not work with other compilers. The Gnu implementation of the standard C library also contains the function qsort_r, which could be used to pass X to the comparison routine, but a more portable way to write the function would be to use an array of pointers rather than an array of indices:

int idxcmp(const void *a,const void *b)
{
    double *ap = *(double *const*)a, *bp = *(double *const*)b;
    if (*ap > *bp) return 1;
    if (*ap < *bp) return -1;
    return ap - bp; 
}

double** sortidx(double *X, size_t n)
{
    double **idx = calloc(n, sizeof(double*));
    for (size_t i=0; i<n; ++i) idx[i] = X + i;
    qsort(idx, n, sizeof(idx[0]), idxcmp);
    return idx;
}

(If you really want to return indices, you can convert the pointer to an index before returning.)

rici
  • 234,347
  • 28
  • 237
  • 341
  • Nice. I also thought to compare the index but I don't know where to put it best. I'll try it. Thank you very much. ^^ – fahadh4ilyas Aug 13 '17 at 14:46
1

What you seek is a stable sorting algorithm. You can stabilize qsort in C, but it needs extra work. In C++ std::stable_sort exists.

If you need to stick to C, then you should implement your own stable sort. Here is a list of stable sorting algorithms:

B
Block sort
Bubble sort
Bucket sort
C
Cascade merge sort
Cocktail shaker sort
Counting sort
Cubesort
G
Gnome sort
I
Insertion sort
L
Library sort
M
Merge sort
O
Odd–even sort
Oscillating merge sort
P
Pigeonhole sort
Proxmap sort
R
Radix sort
T
Timsort
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 1
    As the answers to the question you linked say, you *can* stabilize `qsort` by using the original indices to break ties. – interjay Aug 13 '17 at 14:44
  • @interjay I see, thank you, answer updated, what do you think about it now? =) – gsamaras Aug 13 '17 at 17:46