Сортировка N чисел в порядке цифр - PullRequest
7 голосов
/ 01 августа 2010

При заданном диапазоне чисел N Например, [от 1 до 100], отсортируйте числа в цифровом порядке (т. Е.). Для чисел от 1 до 100 сортированный выходной сигнал будет 1 10 100 11 12 13.,,19 2 20 21 ..... 99

Это похоже на сортировку по радиксу, но цифры сортируются в обратном порядке по сравнению с обычной сортировкой по радиксу.

Iпопытался сохранить все цифры в каждом номере в виде связанного списка для более быстрой работы, но это привело к большой сложности пространства.

Мне нужен рабочий алгоритм для вопроса.

Из всех ответов вариант «Преобразование в строку» является опцией, но нет ли другого способа сделать это?Также может быть указан алгоритм сортировки строк, упомянутый выше.

Ответы [ 7 ]

11 голосов
/ 01 августа 2010

Используйте любой алгоритм сортировки, который вам нравится, но сравните числа как строки , а не как числа.Это в основном лексикографическая сортировка регулярных чисел.Вот пример сортировки гномов в C:

#include <stdlib.h>
#include <string.h>

void sort(int* array, int length) {
    int* iter = array;
    char buf1[12], buf2[12];
    while(iter++ < array+length) {
        if(iter == array || (strcmp(itoa(*iter, &buf1, 10), itoa(*(iter-1), &buf2, 10) >= 0) {
            iter++;
        } else {
            *iter ^= *(iter+1);
            *(iter+1) ^= *iter;
            *iter ^= *(iter+1);
            iter--;
        }
    }
}

Конечно, для этого требуется нестандартная функция itoa, присутствующая в stdlib.h.Более стандартной альтернативой будет использование sprintf, но это делает код немного более загроможденным.Возможно, было бы лучше сначала преобразовать весь массив в строки, затем отсортировать, а затем преобразовать его обратно.

Редактировать: Для справки, соответствующий бит здесь strcmp(itoa(*iter, &buf1, 10), itoa(*(iter-1), &buf2, 10) >= 0,заменяет *iter >= *(iter-1).

4 голосов
/ 01 августа 2010

У меня есть решение, но не совсем алгоритм. Все, что вам нужно сделать, - это преобразовать все числа в строки и отсортировать их как строки.

3 голосов
/ 01 августа 2010

Вот как вы можете сделать это с помощью рекурсивной функции (код на Java):

void doOperation(List<Integer> list, int prefix, int minimum, int maximum) {
    for (int i = 0; i <= 9; i++) {
        int newNumber = prefix * 10 + i;
        if (newNumber >= minimum && newNumber <= maximum) {
            list.add(newNumber);
        }
        if (newNumber > 0 && newNumber <= maximum) {
            doOperation(list, newNumber, minimum, maximum);
        }
    }
}

Вы называете это так:

List<Integer> numberList = new ArrayList<Integer>();
int min=1, max =100;
doOperation(numberList, 0, min, max);
System.out.println(numberList.toString());

РЕДАКТИРОВАТЬ:

Я перевел свой код на C ++ здесь :

#include <stdio.h> 

void doOperation(int list[], int &index, int prefix, int minimum, int maximum) {
    for (int i = 0; i <= 9; i++) {
        int newNumber = prefix * 10 + i;
        if (newNumber >= minimum && newNumber <= maximum) {
            list[index++] = newNumber;
        }
        if (newNumber > 0 && newNumber <= maximum) {
            doOperation(list, index, newNumber, minimum, maximum);
        }
    }
}

int main(void) { 
        int min=1, max =100;
        int* numberList = new int[max-min+1];
        int index = 0;
        doOperation(numberList, index, 0, min, max);
        printf("["); 
        for(int i=0; i<max-min+1; i++) {
                printf("%d ", numberList[i]); 
        }
        printf("]"); 
        return 0; 
}

По сути, идея такова: для каждой цифры (0-9) я добавляю ее вмассив, если он находится между minimum и maximum.Затем я вызываю ту же функцию с этой цифрой в качестве префикса.Он делает то же самое: для каждой цифры он добавляет его к префиксу (prefix * 10 + i) и, если он находится между пределами, добавляет его в массив.Останавливается, когда newNumber больше максимального.

2 голосов
/ 01 августа 2010

Оптимизируйте способ хранения чисел: используйте тип в двоичном коде (BCD) , который обеспечивает простой доступ к определенной цифре. Затем вы можете использовать свой текущий алгоритм, который Стив Джессоп правильно определил как сортировка по старшим значащим цифрам .

Я попытался сохранить все цифры в каждом номере в виде связанного списка для более быстрой работы, но это привело кбольшая сложность пространства.

Хранение каждой цифры в связанном списке приводит к потере пространства двумя различными способами:

  1. Только цифра (0-9)требуется 4 бита памяти для хранения, но вы, вероятно, используете от 8 до 64 бит.Тип char или short занимает 8 бит, а int может занимать до 64 бит.Это использует в 2–16 раз больше памяти, чем оптимальное решение!
  2. Связанные списки добавляют дополнительные ненужные накладные расходы памяти.Для каждой цифры вам нужно дополнительно от 32 до 64 бит для хранения адреса памяти следующей ссылки.Опять же, это увеличивает объем памяти, требуемый на одну цифру, в 8–16 раз.

Более эффективное решение для хранения хранит BCD цифр непрерывно в памяти:

  1. BCD использует только 4 бита на цифру.
  2. Сохраняет цифры в непрерывном блоке памяти, например в массиве.Это устраняет необходимость хранить адреса памяти.Вам не нужна способность связанных списков легко вставлять / удалять из середины.Если вам нужна возможность увеличить числа до неизвестной длины, есть другие абстрактные типы данных, которые позволяют это с гораздо меньшими издержками.Например, vector .

Один вариант, если другие операции, такие как сложение / умножение, не важны, - это выделить достаточно памяти для хранения каждой цифры BCD плюс один терминатор BCD.Терминатор BCD может представлять собой любую комбинацию из 4 битов, которая не используется для представления цифры BCD (например, двоичный код 1111).Сохранение этого способа усложнит другие операции, такие как сложение и умножение.

Обратите внимание, что это очень похоже на идею преобразования в строки и лексикографической сортировки этих строк.Целые числа хранятся внутри компьютера в двоичном виде (база 2).Хранение в BCD больше похоже на базу 10 (база 16 на самом деле, но 6 комбинаций игнорируются), а строки похожи на базу 256. Строки будут использовать примерно вдвое больше памяти, но уже есть эффективные функции, написанные для сортировки строк.BCD, вероятно, потребует разработки пользовательского типа BCD для ваших нужд.

2 голосов
/ 01 августа 2010

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

"1" <"10" <"100" <"11" ... </p>

1 голос
/ 01 августа 2010

Если вы не хотите преобразовывать их в строки, но у вас достаточно места для хранения дополнительной копии списка, я бы сохранил наибольшую степень на десять меньше, чем элемент в копии. Это, вероятно, проще всего сделать с помощью цикла. Теперь назовите ваш исходный массив x и полномочия десяти y.

int findPower(int x) {
   int y = 1;
   while (y * 10 < x) {
      y = y * 10;
   }
   return y;
}

Вы также можете вычислить их напрямую

y = exp10(floor(log10(x)));

но я подозреваю, что итерация может быть быстрее, чем преобразования в и из плавающей запятой.

Для сравнения i th и j th элементов

bool compare(int i, int j) {
  if (y[i] < y[j]) {
    int ti = x[i] * (y[j] / y[i]);
    if (ti == x[j]) {
      return (y[i] < y[j]);  // the compiler will optimize this
    } else {
      return (ti < x[j]);
    }
  } else if (y[i] > y[j]) {
    int tj = x[j] * (y[i] / y[j]);
    if (x[i] == tj) {
      return (y[i] < y[j]);  // the compiler will optimize this
    } else {
      return (x[i] < tj);
    }
  } else {
     return (x[i] < x[j];
  }
}

Здесь делается то, что мы умножаем меньшее число на соответствующую степень десяти, чтобы два числа имели равное количество цифр, а затем сравниваем их. если два измененных числа равны, то сравните длины цифр.

Если у вас нет места для хранения массивов y, вы можете вычислять их при каждом сравнении.

В целом вам, вероятно, лучше использовать предварительно оптимизированные процедуры преобразования цифр.

1 голос
/ 01 августа 2010

Редактировать: я пропустил, что это непрерывный диапазон. В этом случае все ответы, которые говорят о сортировке массива, неверны (включая вашу идею, изложенную в вопросе, что это похоже на основную сортировку), и ответ True Soft правильный.

точно так же, как Radix Sort, но только то, что цифры отсортированы в обратном порядке

Хорошо заметили :-) Если вы действительно делаете это таким образом, как ни странно, это называется сортировкой MSD радикс.

http://en.wikipedia.org/wiki/Radix_sort#Most_significant_digit_radix_sorts

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

Конечно, вам не нужно реализовывать его как основную сортировку: вы можете использовать алгоритм сортировки сравнения с соответствующим компаратором. Например, в C, следующее подходит для использования с qsort (если я не испортил это):

int lex_compare(void *a, void *b) {
    char a_str[12];  // assuming 32bit int
    char b_str[12];
    sprintf(a_str, "%d", *(int*)a);
    sprintf(b_str, "%d", *(int*)b);
    return strcmp(a_str,b_str);
}

Не очень эффективно, так как выполняет многократную работу, но прямолинейно.

...