Atharva Naik Ответов: 4

Как оптимизировать код для сортировки массива в C++?


Я написал программу для сортировки массива для задачи Hackerrank median, мой код проходит 3/4 тестовых заданий. Последний тестовый случай завершается неудачей из-за таймаута, когда общее количество элементов массива равно 10001. Как я могу оптимизировать этот код, чтобы предотвратить тайм-ауты с более высокими числами?

#include<iostream>
using namespace std;
int main() {
    int n,j,i,tmp,med;
    cin >> n;
    int *a = (int*) malloc(n * sizeof(int));
    for(i=0; i<n; i++) 
        cin >> a[i];
    for(i=0; i<n-1; i++) {
        for(j=0; j<n-i-1; j++) {
            if(a[j] > a[j+1]) {
                tmp = a[j];
                a[j] = a[j+1];
                a[j+1] = tmp;
            }
        }
    }
    med = a[n/2];
    free(a);
    cout << med;
    return 0;
}


Что я уже пробовал:

Я попытался изменить целое число на long int, но временная сложность осталась прежней.

4 Ответов

Рейтинг:
2

OriginalGriff

Просто: измените алгоритм.
Это простая сортировка пузырьков: это ни в коем случае не "быстрый" алгоритм, и есть гораздо более быстрые версии: Алгоритм сортировки - Википедия[^]

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

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


Richard MacCutchan

У вас есть ощущение, что есть класс студентов, которые все работают над этой проблемой?

OriginalGriff

Не "работает" над этим, нет ... :смеяться:

Рейтинг:
1

Rick York

Измените цикл так, чтобы он выглядел следующим образом :

for( i = 0; i < n - 2; i++ )
{
    for( j = i + 1; j < n - 1; j++ )
    {
        if( a[j] > a[i] )
        {
            tmp = a[j];
            a[j] = a[i];
            a[i] = tmp;
        }
    }
}
Это приведет к тому, что внешний цикл будет выполняться на единицу меньше конца, а внутренний цикл будет выполняться с единицы после индекса внешнего цикла до конца массива. Проблема заключалась в том, что внутренний цикл повторял обработку элементов, которые были оценены ранее, поэтому он тратил время впустую. Кроме того, этот код не проверяет только соседние записи. Он проверяет массив по индексам i и j, которые не всегда будут смежными. Это должно работать значительно быстрее.


Rick York

Я провел некоторое тестирование с помощью этого алгоритма. Это было лишь немного лучше, чем у ОП. Я также обнаружил, что std::sort был намного лучше (когда массив был вектором), а qsort был еще лучше. Ну что ж.

Рейтинг:
1

Stefan_Lang

Если вы хотите найти только медиану, а не полностью упорядоченный массив входных данных, сортировка будет неоптимальной. Простой способ уменьшить число сравнений состоит в том, чтобы разбить массив на значения, которые больше и меньше некоторого заданного тестового значения, а затем рекурсировать процесс в пределах "правильного" суб-массива.

Вы можете найти лучшее описание этой концепции - с дополнительным поворотом - здесь[^Согласно этому сайту, алгоритм гарантированно будет линейным во времени (т. е. O(N)), и поэтому не должно быть сложностей со списком размера N=10001.

По сравнению с этим даже лучшие алгоритмы сортировки не лучше, чем O(N*log(N)) в худшем случае, и, IIRC, в лучшем случае O(N*log(log(N)) в среднем.

Для тех, кто не любит переходить по ссылкам из неизвестных источников, проверьте google или wikipedia на наличие "медианы медиан" или возьмите это краткое пошаговое описание

Цитата:
Алгоритм медианы медиан

Алгоритм берет в список, а показатель-медианная-в-медианы(а, я). Предположим, что все элементы A различны (хотя алгоритм может быть дополнительно обобщен, чтобы разрешить дублирование элементов).

1. Разделите список на подсписки, каждая из которых имеет длину пять (если для последнего списка доступно менее пяти элементов, это нормально).

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

3. используйте алгоритм медианы медианы для рекурсивного определения медианы множества всех медиан.

4. Используйте эту медиану в качестве элемента pivot, x. pivot-это приблизительная медиана всего списка, а затем каждый рекурсивный шаг оттачивает истинную медиану.

5. Упорядочить список таким образом, что все элементы, меньшие X слева X, а все элементы, которые больше, чем X справа. Это называется секционированием. Элементы не находятся в определенном порядке, если они расположены по обе стороны от X.

6. Позвольте K быть “ранг” х смысл для множества чисел s, х-кth наименьшее число В С.

7а. Если I==K, а затем вернуться.
7b. Если i<k, то рекурсия с использованием медианы медиан для нахождения ith количество оставшихся Sub массив
7c. если i>k, рекурсивно используя алгоритм медианы медиан,чтобы найти (i-k)th номер правого поддиапазона.


Рейтинг:
0

Patrice T

Цитата:
Как я могу оптимизировать этот код, чтобы предотвратить тайм-ауты с более высокими числами?

Я согласен с ОГ и Риком, вы используете самый простой из возможных алгоритмов сортировки пузырьков без каких-либо уточнений. И это плохая идея, так как она действительно неэффективна.
Во-первых, вам нужно понять, как работает ваш код, и изменить его таким образом:
int cnt_test= 0;
int cnt_swap= 0;
for(i=0; i<n-1; i++) {
    for(j=0; j<n-i-1; j++) {
        cnt_test++;
        if(a[j] > a[j+1]) {
            cnt_swap++;
            tmp = a[j];
            a[j] = a[j+1];
            a[j+1] = tmp;
        }
    }
}
// and then print the 2 counters

Таким образом, Вы сможете увидеть рабочую нагрузку.
Затем запустите свой код с образцами данных:
1 2 3 4 5 6 7 8 9
9 1 2 3 4 5 6 7 8
2 3 4 5 6 7 8 9 1
1 2 6 5 4 3 7 8 9
Затем те же данные с 20 и 30 значениями

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

Небольшое изучение алгоритмов сортировки поможет вам выбрать лучший из них.