В чём логическая ошибка и возможная проблема с производительностью в следующем Python-алгоритме сортировки (приведите корректный и эффективный вариант и объясните различия по стабильности и использованию памяти)?
def quicksort(a):
if len(a) <= 1: return a
pivot = a[0]
left = [x for x in a if x pivot]
return quicksort(left) + [pivot] + quicksort(right)

3 Окт в 14:37
26 +1
0
Ответы
1

Коротко — две проблемы.

1) Логическая ошибка. В текущей версии вы отбрасываете все элементы, равные pivot, кроме самого pivot:
left = [x for x in a if x < pivot]
right = [x for x in a if x > pivot]
Если в списке были повторы pivot (или вообще несколько элементов со значением pivot), они не попадают ни в left, ни в right и теряются. Пример: a = [2,2,1] → код вернёт [1,2] вместо [1,2,2].

2) Проблема с производительностью/памятью. Текущая реализация:

создаёт новые списки left/right на каждой рекурсии и затем конкатенирует списки → много копирований и временных списков; это дорого по памяти и по времени (значительная константа).всегда берёт pivot = a[0] → для отсортированных или почти отсортированных входов получаем худший случай O(n^2) по времени и глубину рекурсии O(n) (риск переполнения стека).

Что можно сделать

A) Быстрый исправленный «функциональный» вариант (сохранит относительный порядок равных элементов — стабильный):
def quicksort(a):
if len(a) <= 1:
return a
pivot = a[0]
left = [x for x in a if x < pivot]
equal = [x for x in a if x == pivot]
right = [x for x in a if x > pivot]
return quicksort(left) + equal + quicksort(right)

Плюсы: простой, стабильный (компоненты left/equal/right сохраняют порядок).
Минусы: требует создания новых списков на каждом уровне, много копирований → существенные накладные расходы по памяти и времени по сравнению с in-place реализацией.

B) Вариант in-place (эффективнее по памяти и чаще быстрее). Здесь пример с Lomuto-разбиением и случайным выбором опорного элемента:
import random

def quicksort_inplace(a, lo=0, hi=None):
if hi is None:
hi = len(a) - 1
if lo < hi:
p = partition(a, lo, hi)
quicksort_inplace(a, lo, p - 1)
quicksort_inplace(a, p + 1, hi)

def partition(a, lo, hi):

random pivot to avoid худшие случаиpivot_index = random.randint(lo, hi)
a[pivot_index], a[hi] = a[hi], a[pivot_index]
pivot = a[hi]
i = lo
for j in range(lo, hi):
if a[j] <= pivot:
a[i], a[j] = a[j], a[i]
i += 1
a[i], a[hi] = a[hi], a[i]
return i

Плюсы: in-place, не создаёт больших временных списков; средняя сложность O(n log n), средняя глубина стека O(log n).
Минусы: неустойчив (элементы с равными ключами могут менять порядок). Рекурсивная реализация всё ещё рискует глубокой рекурсией в худшем случае (но это резко уменьшается при рандомном pivot или медиане из трёх); на маленьких сегментах имеет смысл переключаться на insertion sort для ускорения.

Рекомендация для Python

Для практических задач используйте встроенный sorted() / list.sort(): это Timsort — стабилен, имеет гарантированную O(n log n) в худшем случае, очень хорошо оптимизирован и экономит память.Если нужно именно quicksort (например, для обучения или специальных требований), используйте in-place вариант с рандомизацией и оптимизациями (cutoff на маленькие подмассивы, итеративный стек или tail-call elimination), если важна минимальная дополнительная память. Если нужна стабильность и простота — функциональный вариант с equal-группой, но с пониманием затрат по памяти.

Краткое резюме по стабильности и памяти

Оригинал (без equal): неверный (теряет дубликаты).Функциональный с equal: стабильный, но требует дополнительных списков (много аллокаций и копирований).In-place quicksort: экономит память (почти O(log n) стек в среднем), быстрее на больших данных, но неустойчив. Built-in sorted() обычно лучше в реальном коде.
3 Окт в 15:11
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир