Skip to content

Commit eaa83a9

Browse files
committed
Fix QuickSort algorithm with 3-way implementation
1 parent 126d5ef commit eaa83a9

File tree

2 files changed

+58
-30
lines changed

2 files changed

+58
-30
lines changed

.editorconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ ktlint_standard_argument-list-wrapping = disabled
1313
ktlint_standard_parameter-list-wrapping = disabled
1414
ktlint_standard_trailing-comma-on-call-site = disabled
1515
ktlint_standard_trailing-comma-on-declaration-site = disabled
16+
ktlint_standard_no-multi-spaces = disabled
Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,69 @@
11
package ru.romanow.algorithms
22

33
class QuickSort {
4-
fun sort(arr: IntArray, low: Int = 0, high: Int = arr.size - 1) {
5-
if (low < high) {
6-
val pivotIndex = partition(arr, low, high)
7-
sort(arr, low, pivotIndex - 1)
8-
sort(arr, pivotIndex + 1, high)
9-
}
10-
}
4+
fun sort(arr: IntArray, low: Int = 0, high: Int = arr.lastIndex) {
5+
if (low >= high) return
6+
7+
val pivot = arr[low] // В качестве опорного элемента берём первый элемент диапазона
8+
var lt = low // lt — граница области элементов, которые строго меньше pivot
9+
var gt = high // gt — граница области элементов, которые строго больше pivot
10+
var index = low
11+
12+
// arr[low .. lt-1] < pivot – слева уже собраны элементы меньше pivot
13+
// arr[lt .. i-1] == pivot – за ними элементы равные pivot
14+
// arr[i .. gt] – посередине остаётся необработанная область
15+
// arr[gt+1 .. high] > pivot - справа уже собраны элементы больше pivot
16+
while (index <= gt) {
17+
when {
18+
arr[index] < pivot -> {
19+
// Если текущий элемент меньше pivot, его нужно переместить в область "< pivot",
20+
// тем самым расширяя область меньших элементов
21+
arr.swap(lt, index)
22+
23+
// После обмена:
24+
// - элемент на позиции lt гарантированно < pivot
25+
// - элемент, пришедший на позицию i, уже был равен pivot
26+
// (так как область [lt .. i-1] — это == pivot), либо это тот же элемент при lt == i
27+
lt++
28+
index++
29+
}
1130

12-
private fun partition(arr: IntArray, low: Int, high: Int): Int {
13-
val pivot = arr[high] // в качестве опрного элемента берем последний
14-
// Когда начинается цикл перебора элементов массива, изначально никаких элементов ещё не сравнивалось,
15-
// поэтому i находится вне границ массива (это значит, что пока нет ни одного элемента, подходящего под
16-
// условие "меньше или равно опорному элементу").
17-
// Далее внутри цикла мы увеличиваем i, если находим элемент, который подходит под данное условие.
18-
// Таким образом, если встречается элемент, удовлетворяющий условию (`if (arr[j] <= pivotValue)`), мы сначала
19-
// увеличиваем i, а потом меняем местами этот элемент с элементом на позиции i. То есть фактически мы постепенно
20-
// формируем отсортированную часть массива, где все элементы слева от i + 1 гарантированно меньше или равны
21-
// опорному элементу
22-
var i = low - 1
23-
24-
// если arr[j] <= pivot, увеличиваем i и меняем местами arr[i] и arr[j]
25-
// таким образом, все элементы <= pivot собираются слева
26-
for (j in low until high) {
27-
if (arr[j] <= pivot) {
28-
i++
29-
arr.swap(i, j)
31+
arr[index] > pivot -> {
32+
// Если текущий элемент больше pivot, его нужно переместить в область "> pivot"
33+
// Меняем местами arr[i] и arr[gt], тем самым расширяя область больших элементов справа
34+
arr.swap(index, gt)
35+
36+
// После обмена:
37+
// - элемент на позиции gt гарантированно > pivot
38+
// - но элемент, пришедший на позицию i, ещё не проверен (он был из неизвестной области)
39+
// Поэтому уменьшаем только gt, а i НЕ увеличиваем — нужно проверить новый arr[i]
40+
gt--
41+
}
42+
43+
else -> {
44+
// Если arr[i] == pivot, он уже находится в корректной средней области
45+
// Просто расширяем область равных элементов, продвигая i вперёд
46+
index++
47+
}
3048
}
3149
}
32-
// в конце arr[i + 1] и arr[high] меняются местами, чтобы поставить pivot на правильную позицию
33-
arr.swap(i + 1, high)
34-
return i + 1
50+
51+
// После завершения цикла выполняется:
52+
// arr[low .. lt-1] < pivot
53+
// arr[lt .. gt] == pivot
54+
// arr[gt+1 .. high] > pivot
55+
//
56+
// Средняя часть полностью состоит из pivot и уже находится на своём окончательном месте — её сортировать не нужно.
57+
58+
// Рекурсивно сортируем только области,
59+
// содержащие элементы строго меньше и строго больше pivot.
60+
sort(arr, low, lt - 1)
61+
sort(arr, gt + 1, high)
3562
}
3663

3764
private fun IntArray.swap(i: Int, j: Int) {
38-
val temp = this[i]
65+
val tmp = this[i]
3966
this[i] = this[j]
40-
this[j] = temp
67+
this[j] = tmp
4168
}
4269
}

0 commit comments

Comments
 (0)