Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
VB-2012 / 2-cеместр / Дневники / Методика / Лекция 2_4. Сортировка.doc
Скачиваний:
12
Добавлен:
26.03.2015
Размер:
332.29 Кб
Скачать
    1. Быстрая сортировка

Быстрая сортировка(quicksort) — рекурсивный алгоритм, который использует подход «разделяй и властвуй». Если сортируемый список больше, чем минимальный заданный размер, процедура быстрой сортировки разбивает его на два подсписка, а затем рекурсивно вызывает себя для сортировки двух подсписков.

Если алгоритм вызывается для подсписка, содержащего не более одного элемента, то подсписок уже отсортирован, и подпрограмма завершает работу.

Иначе, процедура выбирает какой‑либо элемент из списка и использует его для разбиения списка на два подсписка. Она помещает элементы, которые меньше, чем выбранный элементы в первый подсписок, а остальные — во второй, и затем рекурсивно вызывает себя для сортировки двух подсписков.

Основная идея алгоритма состоит в том, что случайным образом выбирается некоторый элемент массива x, после чего массив просматривается слева, пока не встретится элемент a[i] такой, что a[i] > x, а затем массив просматривается справа, пока не встретится элемент a[j] такой, что a[j] < x. Эти два элемента меняются местами, и процесс просмотра, сравнения и обмена продолжается, пока мы не дойдем до элемента x. В результате массив окажется разбитым на две части - левую, в которой значения ключей будут меньше x, и правую со значениями ключей, большими x. Далее процесс рекурсивно продолжается для левой и правой частей массива до тех пор, пока каждая часть не будет содержать в точности один элемент. Понятно, что как обычно, рекурсию можно заменить итерациями, если запоминать соответствующие индексы массива. Проследим этот процесс на примере нашего стандартного массива (таблица).

Пример быстрой сортировки

Начальное состояние массива

8 23 5 65 |44| 33 1 6

Шаг 1 (в качестве x выбирается a[5])

|--------|

8 23 5 6 44 33 1 65

|---|

8 23 5 6 1 33 44 65

Шаг 2 (в подмассиве a[1], a[5] в качестве x выбирается a[3])

8 23 |5| 6 1 33 44 65

|--------|

1 23 5 6 8 33 44 65

|--|

1 5 23 6 8 33 44 65

Шаг 3 (в подмассиве a[3], a[5] в качестве x выбирается a[4])

1 5 23 |6| 8 33 44 65

|----|

1 5 8 6 23 33 44 65

Шаг 4 (в подмассиве a[3], a[4] выбирается a[4])

1 5 8 |6| 23 33 44 65

|--|

1 5 6 8 23 33 44 65

Алгоритм недаром называется быстрой сортировкой, поскольку для него оценкой числа сравнений и обменов является O(n?log n). На самом деле, в большинстве утилит, выполняющих сортировку массивов, используется именно этот алгоритм.

Public Sub QuickSort(List() As Long, ByVal min as Integer, _

ByVal max As Integer)

Dim med_value As Long

Dim hi As Integer

Dim lo As Integer

‘ Если осталось менее 1 элемента, подсписок отсортирован.

If min >= max Then Exit Sub

‘ Выбрать значение для деления списка.

med_value = list(min)

lo = min

hi = max

Do

Просмотр от hi до значения < med_value.

Do While list(hi) >= med_value

hi = hi - 1

If hi <= lo Then Exit Do

Loop

If hi <= lo Then

list(lo) = med_value

Exit Do

End If

‘ Поменять местами значения lo и hi.

list(lo) = list(hi)

‘ Просмотр от lo до значения >= med_value.

lo = lo + 1

Do While list(lo) < med_values

lo = lo + 1

If lo >= hi Then Exit Do

Loop

If lo >= hi Then

lo = hi

list(hi) = med_value

Exit Do

End If

‘ Поменять местами значения lo и hi.

list(hi) = list(lo)

Loop

‘ Рекурсивная сортировка двух подлистов.

QuickSort list(), min, lo - 1

QuickSort list(), lo + 1, max

End Sub

Есть несколько важных моментов в этой версии алгоритма, которые стоит упомянуть. Во‑первых, значение med_valueдля деления списка не входит ни в один подсписок. Это означает, что в двух подсписках содержится на одни элемент меньше, чем в исходном списке. Т.к. число рассматриваемых элементов уменьшается, то в конечном итоге алгоритм завершит работу.

Эта версия алгоритма использует в качестве разделителя первый элемент в списке. В идеале, это значение должно было бы находиться где‑то в середине списка, так чтобы два подсписка были примерно равного размера. Тем не менее, если элементы первоначально почти отсортированы, то первый элемент — наименьший в списке. При этом алгоритм не поместит ни одного элемента в первый подсписок, и все элементы во второй.

В этом случае каждый вызов подпрограммы требует порядка O(N) шагов для перемещения всех элементов во второй подсписок. Т.к. алгоритм рекурсивно вызывает себя N - 1 раз, время его выполнения будет порядка O(N2), что не лучше, чем у ранее рассмотренных алгоритмов. Ситуацию еще более ухудшает то, что уровень вложенности рекурсии алгоритма N - 1. Для больших списков огромная глубина рекурсии приведет к переполнению стека и сбою в работе программы.

Существует много стратегий выбора разделительного элемента. Можно использовать элемент из середины списка. Это может оказаться неплохим выбором, тем не менее, может оказаться и так, что это окажется наименьший или наибольший элемент списка. При этом один подсписок будет намного больше, чем другой, что приведет к снижению производительности до порядка O(N2) и глубокому уровню рекурсии.

Другая стратегия может заключаться в том, чтобы просмотреть весь список, вычислить среднее арифметическое всех значений, и использовать его в качестве разделительного значения. Этот подход будет давать неплохие результаты, но потребует дополнительных усилий. Дополнительный проход со сложностью порядка O(N) не изменит теоретическое время выполнения алгоритма, но снизит общую производительность.

Третья стратегия — выбрать средний из элементов в начале, конце и середине списка. Преимущество этого подхода в быстроте, потому что потребуется выбрать всего три элемента. При этом гарантируется, что этот элемент не является наибольшим или наименьшим в списке, и вероятно окажется где‑то в середине списка.

И, наконец, последняя стратегия, которая используется в программе Sort, заключается в случайном выборе элемента из списка. Возможно, это будет неплохим выбором. Даже если это не так, возможно на следующем шаге алгоритм, возможно, сделает лучший выбор. Вероятность постоянного выпадения наихудшего случая очень мала.

При использовании других методов выбора точки раздела, существует небольшая вероятность того, что при определенной организации списка время сортировки будет порядка O(N2), хотя маловероятно, что подобная организация списка в начале сортировки встретится на самом деле, тем не менее, время выполнения при этом будет определенно порядка O(N2), неважно почему. Это то, что можно назвать «небольшой вероятностью того, что всегда будет плохая производительность».