Това е 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):
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Quicksort { | |
public static void quickSort(int[] arr, int L, int R) { | |
int pivotValue = arr[(L+R) / 2]; | |
int i = L; | |
int j = R; | |
// Partition array | |
while (i <= j) { | |
while (arr[i] < pivotValue) { | |
i++; | |
} | |
while (arr[j] > pivotValue) { | |
j--; | |
} | |
if (i <= j) { | |
int temp = arr[i]; | |
arr[i] = arr[j]; | |
arr[j] = temp; | |
i++; | |
j--; | |
} | |
} | |
// Recursively sort | |
if (L < j) { | |
// ... the left partition | |
quickSort(arr, L, j); | |
} | |
if (i < R) { | |
// ... the right partition | |
quickSort(arr, i, R); | |
} | |
} | |
public static void main(String[] args) { | |
int[] arr = new int[] { 1, 12, 5, 26, 7, 14, 3, 7, 2 }; | |
quickSort(arr, 0, arr.length-1); | |
for (int element : arr) { | |
System.out.print(element + " "); | |
} | |
} | |
} |
Това е за днес! Този пост ме поизмъчи повечко, но ако видите някоя неточност - свиркайте! 😀
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
Изтриване