Това е Quicksort!
Погледнете обяснението за QuickSort в AlgoList - голяма част от информацията в този пост е взета от там (страхотен сайт с много добри обяснения за редица популярни алгоритми).
---
ОПИСАНИЕ:
Quicksort е алгоритъм за сортиране от тип "разделяй и владей" (разделяме задачата за сортиране на по-малки и накрая обединяваме малките решения в едно голямо).
Както казахме, Quicksort е най-подходящ когато искаме да сортираме големи списъци от данни.
Средната му сложност е O(n log n), а в най-лошия случай е O(n^2), макар че в повечето случаи се представя по-добре от други подобни алгоритми.
АЛГОРИТЪМ:
1. Избираме "осов" елемент (нека го означим с P от "pivot")
- елементът с индекс (L+R) / 2 (където L е първият валиден индекс от масива, а R последният) *
2. Подреждаме и разделяме масива по следния начин **:
- всички елементи по-малки от P отиват в лявата част на масива (преди P)
- всички елементи по-големи от P отиват в дясната част на масива (след P)
- всички елементи равни на P могат да останат или в лявата, или в дясната част на масива
3. Сортираме двете разделени части
- прилагаме стъпка 1 и 2 за лявата част
- прилагаме стъпка 1 и 2 за дясната част
* Елементът може да бъде и всеки друг от масива, но по-удобно е да е някъде по средата.
** Не е задължително частите на разделения масив да са с еднакъв брой елементи.
Как става подреждането и разделянето?
1. Имаме 2 брояча - i и j.
В началото i сочи към първия елемент на масива (или подмасива),
a j към последния елемент.
2. Започваме да увеличаваме стойността на i с 1
докато не намерим елемент със стойност по-голяма или равна на P (arr[i] >= P)
3. След като намерим този елемент, започваме да намаляваме стойността на j с 1
докато не намерим елемент със стойност по-малка или равна на P (arr[j] <= P)
4. Ако i <= j:
- разменяме стойностите на arr[i] и arr[j]
- увеличаваме стойността на i с 1
- намаляваме стойността на j с 1
5. Ако i > j, алгоритъмът за подреждане приключва,
- иначе повтаряме стъпки 2, 3 и 4
След подреждането на масива:
- всички елементи преди arr[i] ще са със стойност по-малка или равна на P
- всички елементи след arr[j] ще са със стойност по-голяма или равна на P.
ПРИМЕР:
Нека сортираме следния масив (примерът ще е дългичък 😅):
Индекс
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
Стойност
|
1
|
12
|
5
|
26
|
7
|
14
|
3
|
7
|
2
|
Първата ни работа е да определим осов елемент! Тъй като казахме, че това ще е елемент с индекс (L+R) / 2, а в случая L = 0 и R = 8, това ще е елементът с индекс 4 (и стойност 7), т.е. P = 7.
Сега идва интересната част - подреждането и разделянето на масива.
Нека означим елементите с цветове, за да е по-прегледно:
- със син цвят ще бъде оцветен осовият елемент P
- с червен цвят ще бъде оцветен елементът, който стои на позиция i
- със зелен цвят ще бъде оцветен елементът, който стои на позиция j
В началото i = 0 (индекс на първия елемент), a j = 8 (индекс на последния елемент)
Масивът ще изглежда така:
Индекс
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
Стойност
|
1
|
12
|
5
|
26
|
7
|
14
|
3
|
7
|
2
|
Индекс
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
Стойност
|
1
|
12
|
5
|
26
|
7
|
14
|
3
|
7
|
2
|
Сега, обаче, започваме да намаляваме стойността на j, докато не намерим елемент, за който е вярно, че arr[j] <= 7.
Преди самото намаляване на стойността, проверяваме дали текущият елемент не отговаря на това условие. Оказва се, че елементът отговаря на условието (j = 8, а arr[j] = 2 и е вярно, че arr[j] <= 7).
Сега:
- разменяме стойностите на arr[i] и arr[j] (ще отбележим разменените стойности с удебелен шрифт)
- увеличаваме i с 1
- намаляваме j с 1
След всичко това, масивът ще изглежда така:
Индекс
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
Стойност
|
1
|
2
|
5
|
26
|
7
|
14
|
3
|
7
|
12
|
Може да ви се струва доста работа за толкова проста цел, но всъщност става все по-лесно колкото повече пъти приложите алгоритъма. 😉
Така, продължаваме напред! Започваме увеличаването на i докато не намерим елемент, за който е вярно, че arr[i] >= 7. Първо проверяваме дали arr[i] в момента (i = 2, arr[i] = 5) не отговаря на условието и, за жалост, не отговаря. Увеличаваме i с единица:
Индекс
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
Стойност
|
1
|
2
|
5
|
26
|
7
|
14
|
3
|
7
|
12
|
Масивът ще изглежда така:
Индекс
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
Стойност
|
1
|
2
|
5
|
7
|
7
|
14
|
3
|
26
|
12
|
Направо:
- разменяме arr[i] и arr[j]
- i++
- j--
Масивът ще изглежда така:
Индекс
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
Стойност
|
1
|
2
|
5
|
7
|
3
|
14
|
7
|
26
|
12
|
Стигнахме до интересна ситуация. Сега i = j. Какво правим сега? Абсолютно същото като преди.
По алгоритъм търсим елемент, за който arr[i] >= P (P = 7) и ако е нужно, увеличаваме стойността на i. Виждаме, че в момента arr[i] отговаря на това условие и затова няма да увеличаваме i.
След това проверяваме дали arr[j] <= P и ако не, намаляваме стойността на j с 1 докато не намерим отговарящ на условието елемент. В момента arr[j] > 7, следователно намаляваме j с 1:
Индекс
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
Стойност
|
1
|
2
|
5
|
7
|
3
|
14
|
7
|
26
|
12
|
Най-накрая! i > j! Това означава, че приключихме... с 2-рата стъпка от QuickSort алгоритъма! 😅
Сега трябва да повторим всичко това, но за 2-та подмасива, които се образуват:
- [1, 2, 5, 7, 3]
- [14, 7, 26, 12]
Предлагам ви продължението на този пример в съкратен вариант, за да не се налага да пращаме някой към близката лудница.
---
Ляв подмасив
1) 1, 2, 5, 7, 3
P = 5
1, 2, 5, 7 3
1, 2, 5, 7, 3
1, 2, 3, 7, 5
1, 2, 3, 7, 5
---
1.1) 1, 2, 3 -> сортирана част
---
1.2) 7, 5
P = 7
7, 5
5, 7
---
1.2.1) 5 -> сортирана част
1.2.2) 7 -> сортирана част
1, 2, 3, 7, 5
---
1.1) 1, 2, 3 -> сортирана част
---
1.2) 7, 5
P = 7
7, 5
5, 7
---
1.2.1) 5 -> сортирана част
1.2.2) 7 -> сортирана част
Десен подмасив
2) 14, 7, 26, 12
P = 7
14, 7, 26, 12
14, 7, 26, 12
7, 14, 26, 12
---
2.1) 7 -> сортирана част
---
2.2) 14, 26, 12
P = 26
14, 26, 12
14, 26, 12
14, 12, 26
---
2.1) 14, 12
P = 14
14, 12
12, 14
---
2.2.1) 12 -> сортирана част
2.2.2) 14 -> сортирана част
---
2.2) 26 -> сортирана част
P = 7
14, 7, 26, 12
14, 7, 26, 12
7, 14, 26, 12
---
2.1) 7 -> сортирана част
---
2.2) 14, 26, 12
P = 26
14, 26, 12
14, 26, 12
14, 12, 26
---
2.1) 14, 12
P = 14
14, 12
12, 14
---
2.2.1) 12 -> сортирана част
2.2.2) 14 -> сортирана част
---
2.2) 26 -> сортирана част
* Забележка: Quicksort в някои случаи ще навлезе дори още по-дълбоко в рекурсията. Тук спестявам тези допълнителни извиквания, които се правят след като даденият подмасив (или подподмасив 😁) вече е сортиран.
Накрая просто обединяваме сортираните части (започвайки от левия подмасив) и получаваме сортирания масив:
Индекс
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
Стойност
|
1
|
2
|
3
|
5
|
7
|
7
|
12
|
14
|
26
|
ИМПЛЕМЕНТАЦИЯ (Java):
---
Това е за днес! Този пост ме поизмъчи повечко, но ако видите някоя неточност - свиркайте! 😀
Can't ask better explanation than this article!! Dude awesome explanation. Cleared my doubts and understood the concept. Thanks for sharing.
ОтговорИзтриванеCheers,
http://www.flowerbrackets.com/how-to-implement-quick-sort-in-java-program/
Thank you, man! :D
Изтриване