|
1 | 1 | package ru.romanow.algorithms |
2 | 2 |
|
3 | 3 | 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 | + } |
11 | 30 |
|
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 | + } |
30 | 48 | } |
31 | 49 | } |
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) |
35 | 62 | } |
36 | 63 |
|
37 | 64 | private fun IntArray.swap(i: Int, j: Int) { |
38 | | - val temp = this[i] |
| 65 | + val tmp = this[i] |
39 | 66 | this[i] = this[j] |
40 | | - this[j] = temp |
| 67 | + this[j] = tmp |
41 | 68 | } |
42 | 69 | } |
0 commit comments