Сортировка списка по нескольким вертикальным столбцам - PullRequest
6 голосов
/ 06 октября 2008

Есть ли у кого-нибудь хороший алгоритм для повторной сортировки массива значений (уже предварительно отсортированных), чтобы они могли отображаться в нескольких (N) столбцах и читаться вертикально? Это будет реализовано в .Net, но я бы предпочел что-то переносимое, а не магическую функцию.

Хорошим примером его работы является рендеринг элемента управления ASP.Net CheckBoxList в виде таблицы с вертикальным направлением.

Вот пример ввода и вывода:

Ввод:

Столбцы = 4
Array = {"A", "B", "C", "D", "E", "F", "G"}

Выход:

ACEG
BDF

Спасибо!

Обновлено (подробнее):

Я думаю, что мне, возможно, придется дать немного больше информации о том, что я пытаюсь сделать ... В основном эта проблема возникла из-за использования автоматической привязки CheckBoxList (где вы можете указать столбцы и направление для вывода и он будет выводить таблицу элементов в правильном порядке, используя jQuery / AJAX для создания сетки флажков. Поэтому я пытаюсь продублировать этот макет, используя css с блоками div с заданной шириной (внутри контейнера div с известной шириной), чтобы они переносились после N элементов (или столбцов). Это также можно отобразить в таблице (например, как ASP .Net делает это.)

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

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

А если в массиве недостаточно элементов, чтобы составить хотя бы одну строку, просто выведите элементы в их первоначальном порядке в одну строку.

Некоторые другие входные / выходные данные могут быть:

Столбцы = 3
Array = {"A", "B", "C", "D"}

1036 * ДСА * B

Столбцы = 5
Array = {"A", "B", "C", "D", "E", "F", "G", "H"}

ACEGH
BDF

Столбцы = 5
Array = {"A", "B", "C", "D"}

ABCD

Ответы [ 3 ]

6 голосов
/ 07 октября 2008

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

То, что мой код делает ниже, - это создание матрицы. Мы записываем матрицу сверху вниз, а затем слева направо (и прекращаем заполнять все, кроме первой строки, когда у нас заканчиваются элементы, чтобы заполнить все столбцы первой строки). Затем мы читаем это в другом порядке, слева направо и сверху вниз. По сути, мы делаем транспонирования матрицы , записывая ее в одном порядке, но читая ее в другом порядке. Транспонирование матрицы - это очень элементарная математическая операция (многие трехмерные программы работают с использованием матричных вычислений, а транспонирование на самом деле простая операция). Хитрость в том, как мы изначально заполняем матрицу. Чтобы убедиться, что мы можем заполнить первый столбец в любом случае, независимо от количества желаемых столбцов и размера массива, мы должны прекратить заполнять матрицу в нормальном порядке, если у нас заканчиваются элементы и резервируются все оставшиеся элементы для Первый ряд. Это приведет к выводу, который вы предложили в своем комментарии.

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

int Columns;
char * Array[] = {"A", "B", "C", "D", "E", "F", "G"};

int main (
    int argc,
    char ** argv
) {
    // Lets thest this with all Column sizes from 1 to 7
    for (Columns = 1; Columns <= 7; Columns++) {

        printf("Output when Columns is set to %d\n", Columns);

        // This is hacky C for quickly get the number of entries
        // in a static array, where size is known at compile time
        int arraySize = sizeof(Array) / sizeof(Array[0]);

        // How many rows we will have
        int rows = arraySize / Columns;

        // Below code is the same as (arraySize % Columns != 0), but
        // it's almost always faster
        if (Columns * rows != arraySize) {
            // We might have lost one row by implicit rounding
            // performed for integer division
            rows++;
        }

        // Now we create a matrix large enough for rows * Columns
        // references. Note that this array could be larger than arraySize!
        char ** matrix = malloc(sizeof(char *) * rows * Columns);

        // Something you only need in C, C# and Java do this automatically:
        // Set all elements in the matrix to NULL(null) references
        memset(matrix, 0, sizeof(char *) * rows * Columns );

        // We fill up the matrix from top to bottom and then from
        // left to right; the order how we fill it up is very important
        int matrixX;
        int matrixY;
        int index = 0;
        for (matrixX = 0; matrixX < Columns; matrixX++) {
            for (matrixY = 0; matrixY < rows; matrixY++) {
                // In case we just have enough elements left to only
                // fill up the first row of the matrix and we are not
                // in this first row, do nothing.
                if (arraySize + matrixX + 1 - (index + Columns) == 0 &&
                        matrixY != 0) {
                    continue;
                }

                // We just copy the next element normally
                matrix[matrixY + matrixX * rows] = Array[index];
                index++;
                //arraySize--;
            }
        }

        // Print the matrix exactly like you'd expect a matrix to be
        // printed to screen, that is from left to right and top to bottom;
        // Note: That is not the order how we have written it,
        // watch the order of the for-loops!
        for (matrixY = 0; matrixY < rows; matrixY++) {
            for (matrixX = 0; matrixX < Columns; matrixX++) {
                // Skip over unset references
                if (matrix[matrixY + matrixX * rows] == NULL)
                    continue;

                printf("%s", matrix[matrixY + matrixX * rows]);
            }
            // Next row in output
            printf("\n");
        }
        printf("\n");

        // Free up unused memory
        free(matrix);
    }   
    return 0;
}

Вывод

Output when Columns is set to 1
A
B
C
D
E
F
G

Output when Columns is set to 2
AE
BF
CG
D

Output when Columns is set to 3
ADG
BE
CF

Output when Columns is set to 4
ACEG
BDF

Output when Columns is set to 5
ACEFG
BD

Output when Columns is set to 6
ACDEFG
B

Output when Columns is set to 7
ABCDEFG

Этот код на C должен легко портироваться на PHP, C #, Java и т. Д., В этом нет большой магии, поэтому он в значительной степени универсален, переносим и кроссплатформенен.


Одна важная вещь, которую я должен добавить:

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

if (Columns <= 0) {
   // Having no column make no sense, we need at least one!
   Columns = 1;
} else if (Columns > arraySize) {
   // We can't have more columns than elements in the array!
   Columns = arraySize;
}

Кроме того, вы также должны проверить, что arraySize равен 0, и в этом случае вы можете сразу же выпрыгнуть из функции, так как в этом случае абсолютно ничего не нужно делать для функции :) Добавление этих проверок должно привести к потере кода твердое вещество. * * тысяча двадцать-одна

Работа с NULL Elements в массиве будет работать, кстати, в этом случае в результирующем выводе нет дыр. Элементы NULL просто пропускаются, как будто их нет. Например. давайте использовать

char * Array[] = {"A", "B", "C", "D", "E", NULL, "F", "G", "H", "I"};

Вывод будет

ADFI
BEG
CH

для столбцов == 4. Если вам нужны отверстия , вам нужно создать элемент отверстия.

char hole = 0;
char * Array[] = {"A", "B", &hole, "C", "D", "E", &hole, "F", "G", "H", "I"};

и немного изменить код рисования

    for (matrixY = 0; matrixY < rows; matrixY++) {
        for (matrixX = 0; matrixX < Columns; matrixX++) {
            // Skip over unset references
            if (matrix[matrixY + matrixX * rows] == NULL)
                continue;

            if (matrix[matrixY + matrixX * rows] == &hole) {
                printf(" ");
            } else {
                printf("%s", matrix[matrixY + matrixX * rows]);
            }
        }
        // Next row in output
        printf("\n");
    }
    printf("\n");

Выходные образцы:

Output when Columns is set to 2
A 
BF
 G
CH
DI
E

Output when Columns is set to 3
ADG
BEH
  I
CF

Output when Columns is set to 4
AC H
BDFI
 EG
2 голосов
/ 06 октября 2008

небольшое ОБНОВЛЕНИЕ:

Алгоритм, который я здесь использую, является модифицированным для рисования изображений. Я притворяюсь, что записи массива являются пиксельными данными изображения, а затем рисую изображение слева направо (1. LtoR) и сверху вниз (2. TtoB), однако данные изображения сохраняются из сверху вниз (1. TtoB), а затем слева направо (2. LtoR); Я в другом порядке. Поскольку изображение не может иметь отверстий , это причина, по которой оно не будет работать с 5 или 6 столбцами. С 4 столбцами вывод

ACEG
BDF

Как изображение это выглядит так

OOOO
OOO.

С О, являющимся пикселем изображения и. быть неопределенным пикселем (отсутствующим). Пропавшие могут быть только в конце изображения, а не в его середине. Это означает, что это также может выглядеть следующим образом

OOO
OO.
OO.
OO.

Все отсутствующие пиксели всегда находятся в конце, если вы читаете сначала сверху вниз и , затем слева направо, потому что в этом случае все отсутствующие пиксели следуют непосредственно друг за другом в конце. Если я читаю диаграмму TtoB, а затем LtoR, она должна выглядеть примерно так: «Пиксель, Пиксель, Пиксель, Пиксель, ..., Пиксель, Пропавший, Пропавший, Пропавший, ..., Пропавший», он может никогда не прочитать «Пиксель, Отсутствует, Пиксель "или" Отсутствует, Пиксель, Отсутствует ". Все пиксели вместе и все пропуски тоже.

С 5 столбцами, как следует из комментария, он должен выглядеть следующим образом

ACEFG
BD

Однако, как изображение это будет выглядеть так

OOOOO
OO...

И это не разрешено алгоритмом. Если я прочту это TtoB, а затем LtoR, то будет написано: «Пиксель, Пиксель, Пиксель, Пиксель, Пиксель, Отсутствует, Пиксель, Отсутствует, Пиксель, Отсутствует». И как указано выше, это не допускается алгоритмом. Таким образом, этот простой способ рисования пикселей не будет рисовать столько столбцов, сколько требуется, если рисование такого количества столбцов приведет к дырам в изображении. В этом случае это просто заполнит отверстия, однако это приведет к тому, что будет нарисовано меньше столбцов.

Позвольте мне подумать о решении, которое всегда будет рисовать запрошенные числа пикселей (в отдельном ответе).


Вам совсем не нужно переупорядочивать данные в памяти. Просто распечатайте его в нужном порядке.

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

int Columns = 4;
char * Array[] = {"A", "B", "C", "D", "E", "F", "G"};

int main (
    int argc,
    char ** argv
) {
    // This is hacky C for quickly get the number of entries
    // in a static array, where size is known at compile time
    int arraySize = sizeof(Array) / sizeof(Array[0]);

    // How many rows are we going to paint?
    int rowsToPaint = (arraySize / Columns) + 1;

    int col;
    int row;

    for (row = 0; row < rowsToPaint; row++) {
        for (col = 0; col < Columns; col++) {
            int index = col * rowsToPaint + row;

            if (index >= arraySize) {
                // Out of bounds
                continue;
            }

            printf("%s", Array[index]);
        }
        printf("\n"); // next row
    }
    printf("\n");
    return 0;
}

Примечание. Это прекрасно работает со значением 8 (поэтому все отображается в одной строке) и со значениями 4 и ниже (отлично работает с 3, 2 и 1), но не может работать с 5. Это не ошибка алгоритма, это вина ограничения.

ACEFG
BD

Ограничение говорит, что столбцы читаются сверху вниз, чтобы получить откорректированные отсортированные данные. Но выше " EFG " отсортировано, и оно не сверху вниз, а слева направо. Таким образом, этот алгоритм имеет проблему. Использование столбцов = 3 будет работать

ADG
BE
CF

Использование двух тоже подойдет

AE
BF
CG
D

И все поместят в одну колонку.

1 голос
/ 06 октября 2008

Это похоже на домашнее задание в любом случае

array<String^>^  sArray = {"A", "B", "C", "D", "E", "F", "G"};
double Columns = 4;
double dRowCount = Convert::ToDouble(sArray->Length) / Columns;
int rowCount = (int) Math::Ceiling(dRowCount);
int i = 0;
int shift = 0;
int printed = 0;
while (printed < sArray->Length){
    while (i < sArray->Length){
        if (i % rowCount == shift){
            Console::Write(sArray[i]);
            printed++;
        }
        i++;
    }
    Console::Write("\n");
    i = 0;
    shift++;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...