Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лекции / Лекция_7_алгоритмы_сортировки_продолжение_ipynb_Colab

.pdf
Скачиваний:
0
Добавлен:
28.06.2026
Размер:
1.15 Mб
Скачать

Итоговый результат: [-2, 0, 3, 5, 7]

Реализация алгоритма на Python

Функция для разбиения подпоследовательностей

def partition(sequence, lo_index, hi_index):

"""

РазбиениеЛомуто sequence-сортируемаяпоследовательность lo_index-леваяграницаподпоследовательности

hi_index-праваяграница подпоследовательности Возвращаетиндексопорногоэлемента

"""

#Опорныйэлемент-первыйэлемент подпоследовательности support_=elsequence[loment_index]

#j-индекудас,будутставитьсяэлементыменьше

опорного

#Начинаемсlo_index,таккакнаэтоминдексе

покастоитопорныйэлемент

j=lo_index

 

 

#Проходимповсемэлементамначиная,со

 

следующегопослеопорного

for i

in range(lo_index+

1,hi_index+

1):

#Еслитекущийэлементменьшеопорного

 

if sequence[i]<support_element:

 

 

#Увеличиваемj-расширяемобластьэлементов

меньшеопорного

j+=

1

 

 

 

#Меняемместамитекущийэлемент(sequence[i])с

элеменатомпозицииj

sequence[i],sequence[j]=sequence[j]

,sequence[i]

#Послепроходаставимопорныйэлементнаего

 

правильноеместо(позицияj)

#Меняемместамиопорныйэлемент

(sequence[loсэлемеindex])натомпозицииj

sequence[losequence[j]index],=sequence[j],

sequence[lo_index]

#Возвращаеминдексопорногоэлемента return j

Реализация алгоритма быстрой сортировки

def quick_sort(sequence, lo_index=None, hi_index=None):

"""

 

 

 

Рекурсивнаябыстраясортировка(

 

использованиемразбиенияЛомуто)

sequence-сортируемаяпоследовательность

 

lo_index-леваяграницаподпоследовательности

hi_index-праваяграница

подпоследовательности

"""

 

 

 

#Инициализациялевойграницыприпервомвызове

 

if lo_index

is None:

 

 

lo_index=

0

 

 

#Инициализправойцияграницыприпервомвызове

 

if hi_index

is None:

 

 

hi_index=

len(sequence)-

1

#Базовыйслучайесли:границысомкнулисьили

 

пересеклись

#(подпоследовательнизодногоэлементастьили

 

пустая)

if lo_index>=hi_index:

 

 

return None

 

 

#ВыполняемразбиениеЛомутополучаем, индекс

 

опорногоэлемента

pivot_index=partition(sequence,lo index,hi

 

_index)

#Рекурсивносортируемлевуючасть(элементы

меньшеопорного)

#Леваячастьот:lo_indexдоpivot_index-1

 

 

quick_sort(sequence,lo index,pivot_index-

 

1)

#Рекурсивносортируемправуючасть(элементы

большеопорного)

#Праваячастьот:pivot_index+1доhi_index

 

quick_sort(sequence,pivot index+

 

1,hi_index)

Быстрая сортировка. Трехчастное разбиение

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

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

Трехчастное разбиение Дейкстры

Один из простых для реализации алгоритм троичного разбиения был предложен Дейкстрой. Он предложил использовать три индекса lt , i , gt . После обработки последовательности:

a[lo .. lt-1]элементы меньше опорного a[lt .. gt-1]элементы равные опорному a[gt .. hi] — элементы больше опорного

Разбиение Дейкстры

1.В качестве опорного элемента выбирается первый элемент последовательности ( supportElement ). Объявляются переменные для хранения индексов:

lt = lo

i = lo + 1 gt = hi

2. Выполняем проход по последовательности, пока i <= gt :

Если sequence[i] меньше опорного:

Меняем местами sequence[lt] и sequence[i]

Увеличиваем lt на 1 Увеличиваем i на 1

Если sequence[i] больше опорного:

Меняем местами sequence[gt] и sequence[i]

Уменьшаем gt на 1

Если sequence[i] равен опорному:

Увеличиваем i на 1

3. Возвращаем lt и gt (границы области элементов, равных опорному)

Графическое пояснение алгоритма разбиения Дейкстры

Пошаговый алгоритм

Исходная последовательность: [0, 5, -2, 7, 3]

 

Инициализация: Опорный элемент ( supportElement ) = 0 (первый элемент). lt =

lo_index = 0 , i = lo_index + 1 = 1 ,

gt = hi_index = 4 . Начальное состояние: [0, 5, -2, 7, 3] с указателями lo,

lt, i в начале и gt в конце.

Шаг 1 (i=1, sequence[1]=5): 5 > 0 (больше). Меняем местами sequence[gt]=3 и sequence[i]=5 → [0, 3, -2, 7, 5] .

Уменьшаем gt=3 . i не увеличивается (остаётся 1), так как на позицию i пришёл новый элемент (3).

Шаг 2 (i=1, sequence[1]=3): 3 > 0 (больше). Меняем местами sequence[gt]=7 и sequence[i]=3 → [0, 7, -2, 3, 5] .

Уменьшаем gt=2 . i остаётся 1.

Шаг 3 (i=1, sequence[1]=7): 7 > 0 (больше). Меняем местами sequence[gt]=-2 и sequence[i]=7 → [0, -2, 7, 3, 5] .

Уменьшаем gt=1 .

Шаг 4: Теперь i=1 , gt=1 . Проверяем условие i <= gt (1 <= 1) → выполняем шаг. sequence[1] = -2 . -2 < 0 (меньше).

Меняем местами sequence[lt]=0 и sequence[i]=-2 → [-2, 0, 7, 3, 5] . Увеличиваем lt=1 , i=2 .

Шаг 5: i=2 , gt=1 . Условие i <= gt (2 <= 1) → ложь. Цикл завершён.

Результат разбиения: [-2, 0, 7, 3, 5] , lt=1 , gt=1 . Области: элементы меньше опорного (0) — [-2] (индекс 0); элементы равные опорному — [0] (индекс 1); элементы больше опорного — [7, 3, 5] (индексы 2..4).

Реализация алгоритма на Python

Функция для разбиения подпоследовательностей

def partition(sequence, lo_index, hi_index):

"""

ТрехчастноеразбиениеДейкстры sequence-сортируемаяпоследовательность lo_index-леваяграница hi_index-праваяграница

Возвращаетграницыобластиэлементовравных, опорному

"""

#Опорныйэлемент-первыйэлемент подпоследовательности support_el=sequence[loment_index]

#lt-границаобластиэлементовменьших,опорного

 

lt=lo_index

 

#i-текущийиндексдляпрохода

 

i=lo_index+

1

 

#gt-границаобластиэлементовбольших,опорного

 

gt=hi_index

 

#Проходимпопоследовательностипокатекущий,

индекснепревыситправуюграницу

while i<=gt:

 

#Еслитекущийэлементменьшеопорного

 

if sequence[i]<support_element:

 

 

#Меняемместамиэлеменатомграницеlt

 

sequence[i],sequence[lt]=sequence[l

t],sequence[i]

 

#Сдвигаемlt iвправо

 

i+=

1

 

lt+=

1

 

#Еслитекущийэлементбольшеопорного

 

elif sequence[i]>support_element:

 

 

#Меняемместамиэлеменатомграницеgt

 

sequence[i],sequence[gt]=sequence[g

t],sequence[i]

 

#Сдвигаемgtвлево(iнеувеличиваемтаккак,

новыйэлемещёнтнепроверен)

gt-=

1

 

#Еслитекущийэлементравенопорному

 

else:

 

 

#Простопереходимкследующемуэлементу

 

i+=

1

 

#Возвращаемграницыобластиэлементовравных,

опорному

return [lt,gt]

 

Реализация алгоритма быстрой сортировки

def quick_sort(sequence, lo_index=None, hi_index=None):

"""

Рекурсивнаябыстраясортировкатрёхчастным разбиениемДейкстры sequence-сортируемаяпоследовательность lo_index-леваяграницаподпоследовательности

hi_index-праваяграница

подпоследовательности

"""

 

 

 

#Инициализациялевойграницыприпервомвызове

 

if lo_index

is None:

 

 

lo_index=

0

 

 

#Инициализправойцияграницыприпервомвызове

 

if hi_index

is None:

 

 

hi_index=

len(sequence)-

1

#Базовыйслучайесли:границысомкнулисьили

пересеклись

#(подпоследовательнизодногоэлементастьили

пустая)

if lo_index>=hi_index:

 

 

return None

 

 

#ВыполняемтрёхчастноеразбиениеДейкстры

 

#Функцияpartitionвозвращаетсписок[lt,gt]-

границыобластиэлементовравных,опорному

h=partition(sequence,lo index,hi index)

 

#Рекурсивносортируемлевуючасть(элементы

меньшеопорного)

#Леваячастьот:lo_indexдоlt-(1гдеlt=h[0])

 

quick_sort(sequence,lo index,h[

 

0]- 1)

#Рекурсивносортируемправуючасть(элементы

большеопорного)

#Праваячастьотgt+1: доhi_index(гдеgt=

 

h[1])

quick_sort(sequence,h[

1]+

1,hi_index)

#Элементыравныеопорному(отh[0]доh[1])уже

находятсясвоихместах

#инетребуютдальнейшейсортировки

 

 

Быстрая сортировка. Оптимизация

Модификация алгоритма быстрой сортировки

Для увеличения оптимальности алгоритма можно применить следующую модификацию. Рекурсивное разбиение последовательности до размера 1 заменить переходом в режим сортировки вставками в случае если размер подпоследовательности меньше или равен определенному значению. Оптимальными называются значения 16, 32, 64. При этом сама реализация остается довольно простой.

Реализация алгоритма на Python

Функция для сортировки вставкой

def insertion_sort(sequence, start_index, end_index):

"""

 

 

 

 

Сортировкавставкамидляподмассива

 

sequence[start_index:end_index+1]

sequence-сортируемаяпоследовательность

 

 

start_index-леваяграницасортируемого

 

 

подмассива

end_index-праваяграницасортируемого

 

 

подмассива

"""

 

 

 

 

#Проходимповсемэлементамподмассиваот

 

 

start_indexдоend_index

for i

in range(start_iendex,index+

 

1):

#Запоминаемтекущийэлементкоторый, будем

 

вставляотсортированнуюьчасть

paste_element=sequence[i]

 

 

 

#Сдвигаемэлементывправопока,онибольше

 

вставляемого

#Условиеневышли: залевуюграницуИпредыдущий

 

элементбольшевставляемого

while i>start_index

and sequence[i-

1]>paste_element:

 

#Сдвигаемпредыдущийэлементнаоднупозицию

 

вправо

sequence[i]=sequence[i-

 

1]

 

 

#Перемещаемуказательвлево

 

 

 

i=i-

1

 

 

 

#Вставляемсохранённыйэлементнанайденноеместо sequence[i]=paste_element

Реализация алгоритма быстрой сортировки

def quick_sort(sequence, lo_index=None, hi_index=None):

"""

 

Модифицированнаябыстраясортировка

 

Прималомразмереподпоследовательности

переключнасортировкуетсявставками

sequence-сортируемаяпоследовательность

 

 

lo_index-леваяграницаподпоследовательности

 

hi_index-праваяграница

 

подпоследовательности

"""

 

 

 

 

 

 

#Инициализациялевойграницыприпервомвызове

 

 

if lo_index

is None:

 

 

 

lo_index=

 

 

0

 

 

 

#Инициализправойцияграницыприпервомвызове

 

 

if hi_index

is None:

 

 

 

hi_index=

 

 

len(sequence) -

1

 

#Еслиразмерподпоследовательности<=32

 

(оптимальноезначение)

#переключнасортировкуемсявставками

 

 

if hi_index-lo_index<=

32:

 

 

insertion_sort

(sequence,lo index,hi index

)

return None

 

 

 

 

#Выполняемразбие(напримериеразбиение, Ломуто

 

илиХоара)

#Получаеминдексопорногоэлемента

 

 

 

pivot_index=partition

 

(sequence,lo index,hi index

)

#Рекурсивносортируемлевуючасть(элементы

 

меньшеопорного)

quick_sort

(sequence,lo index,pivot_-index

1)

#Рекурсивносортируемправуючасть(элементы

 

большеопорного)

quick_sort

(sequence,pivot_+index

1,hi_index )

Напишите программный код или сгенерируйте его с помощью искусственного интеллекта.