В приведённом псевдокоде быстрой сортировки pivot выбирается как A[(l+r)/2], но при массиве с большим количеством одинаковых элементов время работы резко ухудшается — объясните, почему это происходит, предложите модификации алгоритма и оцените их сложность

24 Ноя в 12:26
3 +3
0
Ответы
1
Кратко: при выборе опорного элемента как A[(l+r)/2]A[(l+r)/2]A[(l+r)/2] и при большом числе одинаковых элементов стандартная реализация разбиения часто не удаляет эти равные элементы из дальнейших рекурсий, поэтому каждая рекурсия обрабатывает почти тот же массив — получается рекуррентное соотношение типа
T(n)=T(n−1)+O(n)⇒T(n)=O(n2), T(n)=T(n-1)+O(n)\Rightarrow T(n)=O(n^2),
T(n)=T(n1)+O(n)T(n)=O(n2),
то есть квадратичное время. Причина — неудачная обработка элементов, равных pivot, и/или постоянное получение «небалансных» разбиений.
Варианты исправления и их оценка:
1) Трёхдольное (Dutch national flag) разбиение: разделить на три части «меньше», «равно», «больше» и рекурсивно сортировать только «меньше» и «больше». При многих одинаковых элементах это даёт один проход O(n)O(n)O(n) и никаких дальнейших рекурсий (для всех равных — общая сложность O(n)\,O(n)O(n)). В общем среднем случае — O(nlog⁡n)\,O(n\log n)O(nlogn), в худшем теоретическом (при постоянном плохом pivot) — O(n2)\,O(n^2)O(n2).
2) Случайный выбор pivot или предварительная случайная перестановка массива: уменьшает вероятность неблагоприятных последовательностей; ожидаемое время O(nlog⁡n)\,O(n\log n)O(nlogn), худший случай по-прежнему O(n2)\,O(n^2)O(n2) (но с очень малой вероятностью).
3) Median-of-three (медиана из первого, среднего, последнего): обычно улучшает баланс разбиений на «реальных» данных, ожидаемое O(nlog⁡n)\,O(n\log n)O(nlogn), худший — O(n2)\,O(n^2)O(n2).
4) Introsort (комбинация quicksort + heapsort при глубине рекурсии > clog⁡nc\log nclogn): обеспечивает гарантированное худшее время O(nlog⁡n)\,O(n\log n)O(nlogn) и среднее O(nlog⁡n)\,O(n\log n)O(nlogn); дополнительная сложность реализации невелика, память — константная/стек O(log⁡n)O(\log n)O(logn).
5) Выбор медианы с помощью алгоритма median-of-medians для pivot: гарантированно даёт сбалансированные разбиения, итоговое время O(nlog⁡n)\,O(n\log n)O(nlogn) в худшем случае, но с большим константным множителем (дорого в практике).
Альтернатива: если важен детерминированный худший случай и/или стабильность, использовать mergesort (O(nlog⁡n)O(n\log n)O(nlogn) худшее, но требует O(n)O(n)O(n) доп. памяти) или heapsort (O(nlog⁡n)O(n\log n)O(nlogn) худшее, in-place).
Рекомендация на практике: для массивов с большим числом одинаковых элементов — реализовать трёхдольное разбиение (или использовать реализацию std::sort / qsort с 3-way/Bentley–McIlroy) и/или случайную перестановку; для гарантий по худшему случаю — introsort.
24 Ноя в 13:15
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир