Быстрее сортировать список после вставки элементов или добавления их в отсортированный список

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

Ответ 1

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

Если элементы в основном по порядку, вы можете настроить как инкрементное обновление, так и обычную сортировку, чтобы воспользоваться этим, но, честно говоря, обычно это не стоит проблем. (Вы также должны быть осторожны с такими вещами, как уверенность в том, что какой-то неожиданный порядок не может сделать ваш алгоритм намного дольше, q.v. наивный quicksort)

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

Если ничего другого, помните, что доступно много оптимизированных массовых сортировок.

Ответ 2

Обычно гораздо лучше использовать heap. Короче говоря, он разделяет затраты на поддержание порядка между толкателем и сборщиком. Обе операции: O (log n), а не O (n log n), как и большинство других решений.

Ответ 3

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

Эффективность этого решения - O (n + m) + O (m log m), где n - размер исходного списка, а m - количество вставленных элементов.

Изменить:. Поскольку этот ответ не получает никакой любви, я думал, что воспользуюсь примером кода С++. Я предполагаю, что отсортированный список хранится в связанном списке, а не в массиве. Это изменяет алгоритм, чтобы больше походить на вставку, чем на слияние, но принцип тот же.

// Note that itemstoadd is modified as a side effect of this function
template<typename T>
void AddToSortedList(std::list<T> & sortedlist, std::vector<T> & itemstoadd)
{
    std::sort(itemstoadd.begin(), itemstoadd.end());
    std::list<T>::iterator listposition = sortedlist.begin();
    std::vector<T>::iterator nextnewitem = itemstoadd.begin();
    while ((listposition != sortedlist.end()) || (nextnewitem != itemstoadd.end()))
    {
        if ((listposition == sortedlist.end()) || (*nextnewitem < *listposition))
            sortedlist.insert(listposition, *nextnewitem++);
        else
            ++listposition;
    }
}

Ответ 4

В принципе, быстрее создавать дерево, чем сортировать список. Вставками дерева являются O (log (n)) для каждой вставки, что приводит к общему O (nlog (n)). Сортировка в O (nlog (n)).

Вот почему Java имеет TreeMap (в дополнение к реализациям TreeSet, TreeList, ArrayList и LinkedList).

  • TreeSet хранит вещи в порядке сравнения объектов. Ключ определяется интерфейсом Comparable.

  • LinkedList хранит вещи в порядке вставки.

  • ArrayList использует больше памяти, быстрее для некоторых операций.

  • TreeMap аналогичным образом устраняет необходимость сортировки по ключу. Карта построена в последовательности клавиш во время вставок и поддерживается в отсортированном порядке в любое время.

Однако по какой-то причине реализация Java TreeSet довольно медленная, чем использование ArrayList и своего рода.

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

Ответ 5

Я бы сказал, дайте проверить это!:)

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

Это уже показывает одно: ответ на ваши вопросы сильно зависит от используемого вами алгоритма сортировки. Если он будет иметь низкую производительность в почти отсортированных списках, вставка в правильное положение будет намного быстрее, чем добавление в конце, а затем повторная сортировка; и сортировка слияния может быть не вариантом для вас, так как может потребоваться слишком много внешней памяти, если список огромен. BTW Я использовал специальную реализацию сортировки слиянием, которая использует только половину внешнего хранилища для наивной реализации (для которой требуется столько внешнего хранилища, сколько размер массива).

Если сортировка слияния не является опцией, а quicksort не является опцией, то наилучшей альтернативой является, вероятно, сортировка кучи.

Мои результаты: добавление новых элементов просто в конце, а затем повторная сортировка массива была на несколько величин быстрее, чем вставка их в правильное положение. Однако у моего начального массива было 10 mio элементов (отсортировано), и я добавлял еще один mio (unsorted). Поэтому, если вы добавите 10 элементов в массив из 10 миллионов, вставка их правильно намного быстрее, чем повторная сортировка всего. Таким образом, ответ на ваш вопрос также зависит от того, насколько большой начальный (отсортированный) массив и сколько новых элементов вы хотите добавить к нему.

Ответ 6

Это примерно то же самое. Вставка элемента в отсортированный список - O (log N), и для этого для каждого элемента в списке N (таким образом, создание списка) будет O (N log N), который является скоростью быстрой сортировки (или сортировки слияния) который ближе к этому подходу).

Если вы вместо этого вставили их спереди, это будет O (1), но после выполнения быстрой сортировки все равно будет O (N log N).

Я бы пошел с первым подходом, потому что он может быть немного быстрее. Если начальный размер вашего списка, N, намного больше числа элементов для вставки, X, то подход вставки - O (X log N). Сортировка после вставки в начало списка - O (N log N). Если N = 0 (IE: ваш список изначально пуст), скорость вставки в отсортированном порядке или последующая сортировка одинаковы.

Ответ 7

Если список a) уже отсортирован и b) динамический по своей природе, то вставка в отсортированный список всегда должна быть быстрее (найдите нужное место (O (n)) и вставьте (O (1))).

Однако, если список статичен, тогда должно произойти перетасовка оставшейся части списка (O (n), чтобы найти нужное место и O (n), чтобы сдвинуть объекты вниз).

В любом случае вставка в отсортированный список (или нечто вроде дерева двоичного поиска) должна быть быстрее.

O (n) + O (n) всегда должно быть быстрее, чем O (N log n).

Ответ 8

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

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

Ответ 9

Если это .NET и элементы целые, то быстрее добавить их в словарь (или если вы используете .Net 3.0 или выше, используйте HashSet, если вы не против потери дубликатов). Это дает вам автоматическое сортировка.

Я думаю, что строки будут работать одинаково. Красота заключается в том, что вы вводите и сортируете O (1) таким образом.

Ответ 10

(Если список, о котором вы говорите, похож на С# List<T>.) Добавление некоторых значений в правильные позиции в отсортированный список со многими значениями потребует меньше операций. Но если количество добавляемых значений становится большим, это потребует больше.

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

Ответ 11

Вставка элемента в отсортированный список занимает время O(n), а не O(log n). Вы должны найти место, чтобы положить его, принимая O(log n) время. Но тогда вы должны переместить все элементы - принимая O(n) время. Таким образом, вставка при сохранении сортировки - это O(n ^ 2), где, как вставляя их все, а затем сортируя, O(n log n).

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

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

Ответ 12

На высоком уровне это довольно простая проблема, потому что вы можете думать о сортировке, как только повторный поиск. Если вы хотите вставить элемент в упорядоченный массив, список или дерево, вам нужно найти точку, в которую нужно вставить его. Затем вы положили его, по-видимому, по низкой цене. Таким образом, вы могли бы подумать о алгоритме сортировки, просто беря кучу вещей и один за другим, ища правильное положение и вставляя их. Таким образом, сортировка вставки (O (n * n)) представляет собой итерированный линейный поиск (O (n)). Дерево, куча, слияние, радиус и быстрая сортировка (O (n * log (n))) можно рассматривать как итерированный двоичный поиск (O (log (n))). Можно иметь сортировку O (n), если основной поиск - O (1), как в упорядоченной хэш-таблице. (Примером этого является сортировка 52 карточек, перебрасывая их на 52 ящика.)

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

Конечно, если n мало, например 10, все рассуждение глупо.

Ответ 13

Вставка элемента в отсортированный список - O (log n), при сортировке списка O (n log N) Что бы подсказывало, что всегда лучше сортировать сначала, а затем вставлять

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