Рубрики
Без рубрики

Внедрение алгоритма быстрого сорта на Java

Изучите детали алгоритма quickSort, сосредоточившись на реализации Java.

Автор оригинала: baeldung.

1. Обзор

В этом учебнике мы подробно изумим алгоритм quickSort, сосредоточившись на его реализации Java.

Мы также обсудим его преимущества и недостатки, а затем проанализируем его сложность времени.

2. Алгоритм быстрого сорта

Квиксорт это алгоритм сортировки, который использует принцип «разделяй и властвуй» . Он имеет средний O (n log n) сложности и это один из наиболее часто используемых алгоритмов сортировки, особенно для больших объемов данных.

Важно помнить, что Квиксорт не является стабильным алгоритмом. Стабильный алгоритм сортировки — это алгоритм, в котором элементы с теми же значениями отображаются в том же порядке в отсортированного вывода, что и в списке ввода.

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

Наконец, все отсортированные подли списки сливаются, чтобы сформировать конечный выход.

2.1. Шаги алгоритма

  1. Мы выбираем элемент из списка, называемый стержнем. Мы будем использовать его, чтобы разделить список на два подпро списка.
  2. Мы переупорядочим все элементы вокруг стержня – те, с меньшим значением помещаются перед ним, и все элементы больше, чем стержень после него. После этого шага опора находится в окончательном положении. Это важный шаг раздела.
  3. Вышеуказанные шаги повторяются как к подли спискам слева, так и справа от опоры.

Как мы видим, quicksort, естественно, является рекурсивным алгоритмом, как и любой подход «разделяй и властвуй».

Возьмем простой пример, чтобы лучше понять этот алгоритм.

Arr[] = {5, 9, 4, 6, 5, 3}
  1. Допустим, мы выбираем 5 в качестве стержня для простоты
  2. Сначала мы ставим все элементы менее 5 в первую позицию массива: «3, 4, 5, 6, 5, 9»
  3. Затем мы повторим его для левого под массива (3,4), взяв 3 в качестве стержня
  4. Элементов меньше 3 нет
  5. Мы применяем quicksort на подразряде справа от опоры, т.е. {4}
  6. Этот под массив состоит только из одного отсортированного элемента
  7. Мы продолжаем с правой частью исходного массива, No 6, 5, 9 “, пока мы не получим окончательный упорядоченный массив

2.2. Выбор оптимального стержня

Решающим моментом в quickSort является выбор наилучшего стержня. Средний элемент, конечно, является лучшим, поскольку он разделит список на два равных подпростера.

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

3. Реализация в Java

Первый метод quickSort () который принимает в качестве параметров массив для сортировки, первый и последний индекс. Во-первых, мы проверяем индексы и продолжаем только в том случае, если есть еще элементы, которые необходимо сортировать.

Мы получаем индекс отсортированного стержня и используем его для повторного вызова раздел () метод с теми же параметрами, что и quickSort () метод, но с различными индексами:

public void quickSort(int arr[], int begin, int end) {
    if (begin < end) {
        int partitionIndex = partition(arr, begin, end);

        quickSort(arr, begin, partitionIndex-1);
        quickSort(arr, partitionIndex+1, end);
    }
}

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

К концу раздела, все элементы меньше, чем стержень находятся слева от него, и все элементы больше, то поворот находится справа от него. Поворот находится в окончательном отсортированное положение и функция возвращает эту позицию:

private int partition(int arr[], int begin, int end) {
    int pivot = arr[end];
    int i = (begin-1);

    for (int j = begin; j < end; j++) {
        if (arr[j] <= pivot) {
            i++;

            int swapTemp = arr[i];
            arr[i] = arr[j];
            arr[j] = swapTemp;
        }
    }

    int swapTemp = arr[i+1];
    arr[i+1] = arr[end];
    arr[end] = swapTemp;

    return i+1;
}

4. Алгоритмный анализ

4.1. Сложность времени

В лучшем случае алгоритм разделит список на два подли списка одинакового размера. Итак, первая итерация полного n – размер списка нуждается в О(н) . Сортировка оставшихся двух под списков с n/2 элементы 2’O (n/2) каждый. В результате алгоритм «Быстрый Сорт» имеет сложность O (n log n) .

В худшем случае алгоритм выберет только один элемент в каждой итерации, так что O (n) – O (n-1) – О (1) , что равно O (n 2 ) .

В среднем у «КвикСорта» O (n log n) сложности, что делает его пригодным для больших объемов данных.

4.2. КвикСорт против MergeSort

Давайте обсудим, в каком случае мы должны выбрать quickSort над MergeSort.

Хотя и Квиксорт, и Mergesort имеют среднюю сложность времени O (n log n) , Быстрый сорт является предпочтительным алгоритмом, так как он имеет O (журнал(n)) сложность пространства . Mergesort, с другой стороны, требует О(н) дополнительное хранилище, что делает его довольно дорогим для массивов.

Квиксорт требует доступа к различным индексам для своих операций, но этот доступ не является непосредственно возможным в связанных списках, так как нет непрерывных блоков; поэтому для доступа к элементу мы должны итерировать через каждый узел с самого начала связанного списка. Кроме того, Mergesort реализуется без дополнительного пространства для LinkedLists.

В таком случае, накладные расходы увеличивается для Квиксорт и Mergesort, как правило, предпочтительнее.

5. Заключение

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

Это, как правило, “на месте” алгоритм, со средней сложностью времени O(n журнал n).

Еще один интересный момент, чтобы упомянуть, что Java Arrays.sort () метод использует Квиксорт для сортировки массивов примитивов. Реализация использует два стержня и работает гораздо лучше, чем наше простое решение, поэтому для производственного кода обычно лучше использовать библиотечные методы.

Как всегда, код для реализации этого алгоритма можно найти в течение в нашем репозитории GitHub .