Как «сортировать и изменять» список?

Я не уверен, что эта тема уже обсуждалась, но если это так, у меня не было подходящего ключевого слова, чтобы найти ее. В моем коде я извлекаю сопоставление, скажем: {5,0,23,2,2,0}. Как видите, значения не являются смежными. Что я хотел бы сделать, так это преобразовать это сопоставление, чтобы возвращаемый список был {3,0,4,1,2,0}. Здесь 0 означает, что элемент следует игнорировать. Итак, в основном, что я хочу сделать, это перенумеровать элементы в зависимости от их порядка, не перемещая их в списке.

Я сделал это, сначала создав копию исходного массива, а затем отсортировав эту копию с помощью qsort из библиотеки C stdlib. Я также создал return_list, в котором будет храниться мое окончательное сопоставление.

(исходный массив vec)

    elt_count = 1;
    for (size_t i = 0; i < n; i++) {
        if (copy[i] != 0) {
            for (size_t j = 0; j < n; j++) {
                if (copy[i] == vec[j]) {
                    return_list[j] = elt_count;
                    vec[j] = 0;
                    elt_count++;
                    break;
                }
            }
        }
    }

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

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

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

vec = (5,0,23,2,2,0)
5: (0)
23: (2)
2: (3,4)

Однако я не уверен, как это реализовать, а затем использовать для повышения производительности.

Конечно, любые другие идеи по улучшению производительности приветствуются!

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

Alex 04.04.2023 10:31

@ Алекс, да, это то, что я пытаюсь сделать. (это кажется намного проще, как вы это сказали, я, возможно, слишком усложнил :))

Unemployed Goose 04.04.2023 10:42
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
1
2
86
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Один простой подход - сделать копию вашего массива, включая индексы значений:

int a2[n][2];
int zeros = 0;
for (int i = 0; i < n; i++) {
    a2[i - zeros][0] = vec[i];
    a2[i - zeros][1] = i;
    zeros += (vec[i] == 0);
}

Теперь a2 — это массив длины n-zeros, а его элементы — ненулевые элементы vec в паре с их индексами.

Теперь сортируйте a2:

qsort(a2, n - zeros, sizeof(a2[0]), cmp_a2);

Используя эту функцию сравнения:

int cmp_a2(const void *v, const void*w) {
    const int *a = v;
    const int *b = w;
    if (a[0] < b[0]) return -1;
    if (a[0] > b[0]) return 1;
    if (a[1] < b[1]) return -1;
    if (a[1] > b[1]) return 1;
    return 0;
}

Теперь вы можете заменить исходные элементы в массиве, используя исходные индексы, хранящиеся в отсортированном a2:

for (int i = 0; i < n - zeros; i++) {
    vec[a2[i][1]] = i + 1;
}

Вот все части вместе как полный рабочий пример:

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

void print_array(int *a, size_t n) {
    for (int i = 0; i < n; i++)
        printf("%s%d", i>0 ? ", " : "", a[i]);
    printf("\n");
}

int cmp_a2(const void *v, const void*w) {
    const int *a = v;
    const int *b = w;
    if (a[0] < b[0]) return -1;
    if (a[0] > b[0]) return 1;
    if (a[1] < b[1]) return -1;
    if (a[1] > b[1]) return 1;
    return 0;
}

void order_array(int *vec, size_t n) {
    int (*a2)[2] = malloc(n * 2 * sizeof(int));
    size_t zeros = 0;
    for (int i = 0; i < n; i++) {
        a2[i - zeros][0] = vec[i];
        a2[i - zeros][1] = i;
        zeros += (vec[i] == 0);
    }
    qsort(a2, n - zeros, sizeof(a2[0]), cmp_a2);

    for (int i = 0; i < n - zeros; i++) {
        vec[a2[i][1]] = i + 1;
    }
    free(a2);
}

int main(int argc, char *argv[]) {
    int a[] = {5,0,23,2,2,0};
    size_t n = sizeof(a) / sizeof(*a);
    print_array(a, n);
    order_array(a, n);
    print_array(a, n);
}

Спасибо за ваш быстрый ответ ! У меня есть 3 ошибки компиляции при компиляции вашего кода. Один здесь: const int *a = v; и строка ниже и еще один здесь: int (*a2)[2] = malloc(n * 2 * sizeof(int));. Первую исправил так: const int* a = (int*)v;. Я даже не знаю, не сломает ли это ваш код, а для второго я не знаю, как его исправить. Вот сообщение об ошибке: Преобразование из "void*" в указатель на не-"void" требует явного приведения

Unemployed Goose 04.04.2023 11:05

Вы уверены, что используете компилятор C (а не компилятор C++) для компиляции кода? Программа компилируется без предупреждения с использованием параметров -ansi -pedantic -Wall -std=c99 как в clang, так и в gcc.

Paul Hankin 04.04.2023 11:10

Теперь, когда вы это сказали, я думаю, что работаю с компилятором C++. Код, над которым я работаю, написан на C, но инструменты компиляции, используемые моей командой, — C++. Я собираюсь попытаться найти способ заставить его работать с этим компилятором. Во всяком случае, теперь яснее, что вы дали мне этот метод. Благодарю за ваш ответ !

Unemployed Goose 04.04.2023 11:18

Примечание. a2 — это указатель на массив, что может затруднить поиск правильного синтаксиса приведения. Вы можете использовать static_cast<decltype(a2)>(malloc(...)) вместо malloc(...), чтобы заставить компилятор C++ принять этот синтаксис C.

anatolyg 04.04.2023 11:36

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

Unemployed Goose 04.04.2023 13:57

Мне просто нужно одно уточнение о вашей функции сравнения. Итак, если a[0] < b[0], он возвращает 1, а если наоборот, возвращает -1. А две строчки внизу сделаны для случаев, когда a[0] = b[0] это правильно?

Unemployed Goose 04.04.2023 14:48

Да, для одинаковых значений (т. е.: a[0] == b[0]) он сравнивает исходные индексы, чтобы дубликаты в исходном массиве получали последовательные значения, причем крайний левый получает наименьшее значение, а крайний правый — наивысшее значение и так далее.

Paul Hankin 04.04.2023 14:57

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

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

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])

int ElemDiff(const void *leftElemPtr, const void *rightElemPtr) {
    return *(int *) leftElemPtr - *(int *) rightElemPtr;
}

int main(void)
{
    int vec[] = {5, 0, 23, 2, 2, 0};
    int sortedVec[LEN(vec)];
    int i, *tail;

    memcpy(sortedVec, vec, sizeof (vec));
    qsort(sortedVec, LEN(sortedVec), sizeof sortedVec[0], ElemDiff);
    for (i = 0; i < LEN(vec); i++) {
        tail = bsearch(vec + i, sortedVec, LEN(sortedVec), sizeof sortedVec[0], ElemDiff);
        assert(tail != NULL);
        printf("%lld ", tail - sortedVec);
    }
    putchar('\n');

    return 0;
}

Это напечатает

4 0 5 2 2 0

Желаемое решение, данное в вопросе, 3, 0, 4, 1, 2, 0 не 4, 0, 5, 2, 2, 0.

Paul Hankin 04.04.2023 11:42

Да, поэтому я сказал "вот начало".

August Karlstrom 04.04.2023 11:46

Другие вопросы по теме