Сортировка массива C на основе содержимого другого массива

#c #arrays #sorting

#c #массивы #сортировка

Вопрос:

Я пытаюсь отсортировать массив, A элементами которого являются индексы. Индексы ссылаются на другой массив, B значение которого будет определять порядок A . Итак, я хотел бы отсортировать A таким образом, чтобы B[ A[i] ] количество увеличивалось.

Например:

A = [0, 1, 4, 5, 7]
B = [5, 3, 8, 2, 2, 7, 1, 6, 3, 9]

Сортировка A будет

A' = [ 7, 4, 1, 0, 5 ]

Возможно ли это с помощью встроенной сортировки C, или мне придется написать свою собственную реализацию?

РЕДАКТИРОВАТЬ: Эти массивы являются локальными функциональными переменными.

Комментарии:

1. Были бы уродливые хаки, которые могли бы заставить это работать с qsort, но я думаю, вам лучше написать свою собственную версию..

2. @Pavan Я бы предпочел сражаться с зомби в пустыне, а не использовать мою собственную процедуру сортировки в производстве.

3. Это не уродливый взлом. Просто есть функция, которая сопоставляет значение массива с порядком сортировки. В этом случае функция может быть дополнена поиском по массиву, но вряд ли это необычное требование.

Ответ №1:

Если вы хотите использовать qsort , лучше всего было бы повторно обернуть индексы в A и значения в B в структуру, а затем создать компаратор на основе нового массива, который содержит структуру. Например:

 typedef struct
{
    int index_from_A;
    int value_from_B;
} index_value_wrapper;

index_value_wrapper index_wrapper_array[5];

for (int i=0; i < 5; i  )
{
    index_wrapper_array[i].index_from_A = A[i];
    index_wrapper_array[i].value_from_B = B[A[i]];
}

int comparitor (const void* lhs, const void* rhs)
{
    return (lhs.value_from_B - rhs.value_from_B);
}
  

Теперь вы можете запустить qsort массив struct и оттуда извлечь нужную вам отсортированную последовательность для исходного массива A , не прибегая к пользовательской функции сортировки.

Ответ №2:

Если он у вас есть, qsort_r предоставляет способ сделать это. Вы можете предоставить ему контекстную информацию в дополнительном параметре. Этот контекст передается функции сравнения. Вы можете получить доступ к этой дополнительной информации, чтобы извлечь желаемую информацию о сортировке.

Компилятор Microsoft имеет аналогичный: qsort_s

Ответ №3:

Я думаю, вы можете использовать qsort и пользовательский компаратор

 int comparator(const void *x, const void *y)
{
    return ( b[*(int*)x] - b[*(int*)y] );
}
  

Комментарии:

1. Есть ли какой-либо способ передать ссылки на массив в компаратор?

2. @ajwood Я не уверен, что понимаю ваш вопрос

3. Ajwood, если ты имеешь в виду индексы массива, то нет — элементы массива перемещаются по мере выполнения сортировки

4. @cnicutar: Ваш ответ был бы идеальным… но эти массивы являются локальными переменными в функции, поэтому компаратор не может проверить значения B

5. @ajwood: Из того, что я вижу в приведенном выше коде, b должен быть некоторый тип глобально доступного массива.

Ответ №4:

Создайте другой массив C типа struct { int a_value; int b_value} , инициализируйте каждый элемент значениями каждого индекса a и значением, полученным из b. Отсортируйте это, пройдитесь по отсортированному C, скопировав a_values обратно в A.

Виола. Нет, это большая скрипка. Вуаля!

Ответ №5:

Используйте ваше правило в качестве функции сравнения с qsort (при условии, что B длиннее, чем A):

 #include <stdio.h>
#include <stdlib.h>

int A[] = {0, 1, 4, 5, 7};
int B[]= {5, 3, 8, 2, 2, 7, 1, 6, 3, 9};

 int my_cmp(const void *a_, const void *b_,void *arg_)
 {
   const int *a = a_, *b = b_;

   if(B[*a] == B[*b])
       return 0;
    else if (B[*a] < B[*b])
        return -1;
    else
        return 1;

}

int main(int argc,char *arga[])
{
    int i;

    qsort(A,sizeof A/sizeof A[0] ,sizeof A[0],my_cmp);

    puts("Sorted A");
    for(i = 0 ; i < sizeof A/sizeof A[0]; i  ) {
        printf("A[%d] : %d B[A[%d]] : %dn",i,A[i],i,B[A[i]]);
    }

    return 0;
}
  

Это дает:

 $ ./a.out
Sorted A
A[0] : 4 B[A[0]] : 2
A[1] : 1 B[A[1]] : 3
A[2] : 0 B[A[2]] : 5
A[3] : 7 B[A[3]] : 6
A[4] : 5 B[A[4]] : 7
  

На многих платформах также доступен qsort_r (в Linux вам придется #define _GNU_SOURCE перед включением <stdlib.h> использовать его. Используя это, вы бы изменили функцию сравнения на, например

 int my_cmp(const void *a_, const void *b_,void *arg_)
 {
    const int *a = a_, *b = b_, *arg = arg_;

   if(arg[*a] == arg[*b])
       return 0;
    else if (arg[*a] < arg[*b])
        return -1;
    else
        return 1;

}
  

И вызовите qsort_r как

 qsort_r(A,sizeof A/sizeof A[0] ,sizeof A[0],my_cmp,B);
  

Комментарии:

1. Это работает хорошо, пока оно не является многопоточным. Если он не является однопоточным и ссылочные данные (B) не являются статическими, то это не сработает.

2. @Paul qsort — единственная стандартная функция сортировки в C, и она работает подобным образом, в противном случае вам придется создавать свою собственную или использовать что-то нестандартное, например qsort_r — добавлен пример для этого. Если вы не находитесь в многопоточной среде, A и B вполне могут быть локальными. Вам просто нужно установить глобальное int * значение для вашего массива B перед вызовом qsort

3. @nos, стандартный или нет, если это не работает, вы не сможете его использовать 🙂 Работает глобальный (хотя это немного попахивает кодом) или используйте структуру-оболочку, как предлагали другие (включая меня).

4. @Нет, да, это так. Но это не было частью вашего ответа или комментария, когда я добавлял свой комментарий… qsort_r — это определенно правильный путь, если он доступен