Сортировка подсчетом - Эффективность

Я думал о сортировке подсчетом и о том, как мы ее реализуем, на самом деле, как работает алгоритм. Я застрял с одной частью, алгоритм действительно прост и понятен, но одна его часть не кажется необходимой. Я думал, что люди могут ошибаться или около того, но похоже, что все используют один и тот же метод, поэтому я где-то ошибаюсь. Не могли бы вы объяснить.

Вот код для подсчета сортировки от geeksforgeeks

    // C Program for counting sort
#include <stdio.h>
#include <string.h>
#define RANGE 255

// The main function that sort the given string arr[] in
// alphabatical order
void countSort(char arr[])
{
    // The output character array that will have sorted arr
    char output[strlen(arr)];

    // Create a count array to store count of inidividul
    // characters and initialize count array as 0
    int count[RANGE + 1], i;
    memset(count, 0, sizeof(count));

    // Store count of each character
    for(i = 0; arr[i]; ++i)
        ++count[arr[i]];

    // Change count[i] so that count[i] now contains actual
    // position of this character in output array
    for (i = 1; i <= RANGE; ++i)
        count[i] += count[i-1];

    // Build the output character array
    for (i = 0; arr[i]; ++i)
    {
        output[count[arr[i]]-1] = arr[i];
        --count[arr[i]];
    }

    // Copy the output array to arr, so that arr now
    // contains sorted characters
    for (i = 0; arr[i]; ++i)
        arr[i] = output[i];
}

// Driver program to test above function
int main()
{
    char arr[] = "geeksforgeeks";//"applepp";

    countSort(arr);

    printf("Sorted character array is %s\n", arr);
    return 0;
}

Круто, но об этой части:

// Build the output character array
        for (i = 0; arr[i]; ++i)
        {
            output[count[arr[i]]-1] = arr[i];
            --count[arr[i]];
        }

Зачем мне это?? Хорошо, я посчитал свои числа:

Допустим, у меня был массив -> [1, 3, 6, 3, 2, 4]

         INDEXES     0  1  2  3  4  5  6
  I created this -> [0, 1, 1, 2, 1, 0, 1]

Чем эта часть делает это:

  [0, 1+0, 1+1, 2+2, 4+1, 0+5, 1+5]
  [0, 1, 2, 4, 5, 5, 6]

НО ПОЧЕМУ ??

Разве я не могу просто использовать свой массив, как раньше? Вот моя идея и мой код, пожалуйста, объясните, почему это неправильно или почему другой способ более полезен.

void countingSort (int *arr) {

    int countingArray[MAX_NUM] = {0};

    for (i = 0 ; i < ARRAY_SIZE ; i++)
        countingArray[arr[i]]++;

    int output_Index = 0;

    for (i = 0 ; i < MAX_NUM ; i++)
        while ( countingArray[i]-- )
            arr[output_Index++] = i;
}

person Community    schedule 01.02.2017    source источник


Ответы (2)


Для простого случая, когда вы сортируете массив целых чисел, ваш код проще и лучше.

Однако сортировка подсчетом — это общий алгоритм сортировки, который может сортировать на основе ключа сортировки, полученного из сортируемых элементов, который используется для их сравнения, в отличие от прямого сравнения самих элементов. В случае массива целых чисел элементы и ключи сортировки могут быть одними и теми же, вы просто сравниваете их напрямую.

Мне кажется, что код geeksforgeeks был адаптирован из более общего примера, который позволяет использовать ключи сортировки, что-то вроде этого:

// Store count of each item
for(i = 0; arr[i]; ++i)
    ++count[key(arr[i])];

// Change count[i] so that count[i] now contains actual
// position of this character in output array
for (i = 1; i <= RANGE; ++i)
    count[i] += count[i-1];

// Build the output array
for (i = 0; arr[i]; ++i)
{
    output[count[key(arr[i])]-1] = arr[i];
    --count[key(arr[i])];
}

Где key — это функция, которая вычисляет ключ сортировки на основе элемента (для целочисленного типа вы можете просто вернуть само целое число). В этом случае MAX_NUM нужно заменить на MAX_KEY.

Этот подход использует дополнительный выходной массив, поскольку окончательный результат создается путем копирования элементов из arr, а не просто из информации в count (которая содержит только количество элементов с каждым ключом). Однако возможна сортировка подсчетом на месте.

Алгоритм также гарантирует стабильную сортировку (элементы с одинаковым ключом сортировки имеют относительный порядок сохраняется при сортировке) - это бессмысленно при сортировке целых чисел.

Однако, поскольку они убрали возможность сортировки по ключу, нет причин для дополнительной сложности, и ваш способ лучше.

Также возможно, что они скопировали код из такого языка, как C++, где приведение типа int (которое будет вызываться при использовании элемента для индексации массива) могло быть перегружено для возврата ключа сортировки, но по ошибке было преобразовано в C.

person samgak    schedule 01.02.2017

Я думаю, что ваш вариант является лучшим подходом. Я подозреваю, что человек, написавший этот пример кода, вероятно, написал аналогичные примеры кода для других алгоритмов сортировки — существует множество алгоритмов сортировки, где вам действительно требуется отдельное «рабочее пространство» — и он недостаточно подумал в этот.

В качестве альтернативы, он(а) мог(а) посчитать, что алгоритм легче объяснить, если мы отделим «генерирование результата» от «перемещения результата на место»? Я не согласен, если это так, но подробные комментарии ясно показывают, что он имел в виду педагогику.

Тем не менее, есть несколько незначительных проблем с вашей версией:

  • Вы забыли объявить i.
  • Вы должны использовать длину массива в качестве параметра, а не использовать жестко запрограммированный ARRAY_SIZE. (В примере кода этой проблемы можно избежать, используя строку, поэтому они могут выполнять итерацию до завершающего нулевого байта.)
  • Это может быть субъективно, но вместо while ( countingArray[i]-- ), я думаю, понятнее будет написать for (int j = 0; j < countingArray[i]; ++j).
person ruakh    schedule 01.02.2017
comment
Более субъективно, memset? - person Mooing Duck; 01.02.2017
comment
Мне нравится ответ. Тем не менее, мой код был для конкурса, поэтому я определил общие переменные, например, MAX_NUM на самом деле находится в основной функции, и я также определен в общем, я не люблю помещать слишком много параметров в функцию, если это не необходимо. - person ; 01.02.2017
comment
@MooingDuck А как насчет memset? - person ; 01.02.2017
comment
@BedirTapkan: while ( countingArray[i]-- ) arr[output_Index++] = i; можно заменить одним вызовом memset. - person Mooing Duck; 01.02.2017
comment
@MooingDuck Maan :D Я никогда не использовал memset таким образом, здорово! Спасибо! - person ; 01.02.2017
comment
@MooingDuck: функция OP, в отличие от версии geeksforgeeks, сортирует массив int; Не думаю, что memset для этого подойдет. - person ruakh; 02.02.2017
comment
C++ std::fill работает нормально, но вы правы, я не могу найти ничего эквивалентного для C. Ему придется сделать собственный вариант. - person Mooing Duck; 02.02.2017