Опубликован: 05.01.2015 | Доступ: свободный | Студентов: 2065 / 0 | Длительность: 63:16:00
Лекция 6:

Элементарные методы сортировки

Сортировка выбором

Один из самых простых алгоритмов сортировки работает следующим образом. Сначала находится наименьший элемент массива и меняется местами с элементом, стоящим первым в сортируемом массиве. Потом находится второй наименьший элемент и меняется местами с элементом, стоящим вторым в исходном массиве. Этот процесс продолжается до тех пор, пока весь массив не будет отсортирован. Данный метод называется сортировкой выбором (selection sort), поскольку он все время выбирает наименьший элемент из числа не отсортированных. На рис 6.2 представлен пример работы этого метода.

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

Рис. 6.2. Пример сортировки выбором

В этом примере первый проход ничего не меняет, поскольку в массиве нет элемента, меньшего самого левого элемента А. На втором проходе наименьшим среди оставшихся оказался другой элемент А, поэтому он меняется местами с элементом S, занимающим вторую позицию. На третьем проходе элемент Е из середины массива обменивается с О в третьей позиции, на четвертом проходе еще один элемент Е меняется местами с R в четвертой позиции и т.д.

Программа 6.2 — реализация сортировки выбором, в которой выдержаны все принятые нами соглашения. Внутренний цикл содержит только сравнение текущего элемента с наименьшим выявленным на данный момент элементом (плюс код, необходимый для сдвига индекса текущего элемента и проверки, что он не выходит за границы массива). Проще не придумаешь. Действия по перемещению элементов находятся вне внутреннего цикла: каждая операция обмена элементов устанавливает один из них в окончательную позицию, так что всего необходимо N— 1 таких операций (для последнего элемента обмен не нужен). Поэтому время выполнения определяется в основном количеством сравнений. В разделе 6.5 мы покажем, что это количество пропорционально N 2, научимся предсказывать общее время выполнения и узнаем, как сравнивать сортировку выбором с другими элементарными методами.

Программа 6.2. Сортировка выбором

Для каждого i от l до r-1 элемент a[i] меняется местами с минимальным элементом из a[i], ..., a[r]. По мере продвижения индекса i слева направо элементы слева от него занимают свои окончательные позиции в массиве (и больше не перемещаются), поэтому, когда i достигнет правого конца, массив будет полностью отсортирован.

template <class Item>
void selection(Item a[], int l, int r)
  { for (int i = l; i < r; i++)
      { int min = i;
        for (int j = i+1; j <= r; j++)
if (a[j] < a[min]) min = j;
  exch(a[i], a[min]);
      }
  }
      

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

Несмотря на простоту и очевидный примитивизм, сортировка выбором превосходит более совершенные методы в одном важном случае: когда элементы очень велики, а ключи очень малы. В подобных приложениях стоимость перемещения данных намного превосходят стоимость сравнения, а никакой алгоритм не способен упорядочить файл с заметно меньшим числом перемещений данных, чем сортировка выбором (см. лемму 6.5 в разделе 6.5).

Упражнения

6.11. Покажите в стиле рис. 6.2 процесс упорядочения файла E A S Y Q U E S T I O N методом выбора.

6.12. Каково максимальное количество обменов любого конкретного элемента в процессе сортировки выбором? Чему равно среднее количество обменов, приходящееся на один элемент?

6.13. Приведите пример файла из N элементов, в котором во время выполнения сортировки выбором условие a[j] < a[min] неверно максимальное количество раз (когда min изменяет значение).

6.14. Является ли сортировка выбором устойчивой?

Сортировка вставками

Картежники часто упорядочивают карты в руках следующим образом: выбирают по очереди каждую карту и вставляют ее в нужное место среди уже отсортированных элементов. В компьютерной реализации потребуется освободить место для вставляемого элемента, сдвинув большие элементы на одну позицию вправо и переместив на освободившееся место вставляемый элемент. Функция sort из программы 6.1 является программной реализацией этого метода, получившего название сортировка вставками (insertion sort).

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

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

Прежде всего, можно отказаться от выполнения операций compexch, когда встречается ключ, не больший ключа вставляемого элемента, поскольку подмассив, находящийся слева, уже отсортирован. А именно, при выполнении условия a[j-1] < a[j] можно выполнить команду break, чтобы выйти из внутреннего цикла for в функции sort программы 6.1. Это изменение превращает реализацию в адаптивную сортировку и примерно вдвое ускоряет работу программы для случайно упорядоченных ключей (см. лемму 6.2).

После усовершенствования, описанного в предыдущем абзаце, получилось два условия прекращения выполнения внутреннего цикла — для наглядности можно заменить его на оператор while. Менее очевидное улучшение реализации следует из того факта, что условие j > l обычно оказывается излишним: ведь оно выполняется только когда вставляемый элемент является наименьшим из просмотренных к этому моменту и поэтому доходит до начала массива.

 Пример выполнения сортировки вставками

Рис. 6.3. Пример выполнения сортировки вставками

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

Часто применяется альтернативный способ: сортируемые ключи хранятся в элементах массива от a[1] до a[N], а в a[0] заносится сигнальный ключ (sentinel key), значение которого по крайней мере не больше наименьшего ключа в сортируемом массиве. Тогда проверка, найден ли меньший ключ, проверяет сразу оба условия, и в результате внутренний цикл становится меньше, а быстродействие программы повышается.

Сигнальные ключи не всегда удобны: иногда бывает трудно определить значение минимально возможного ключа, а иногда в вызывающей программе нет места для дополнительного ключа. В программе 6.3 предлагается один из способов обойти сразу обе эти проблемы: сначала выполняется отдельный проход по массиву, который помещает в первую позицию элемент с минимальным ключом. Затем сортируется остальной массив, а первый, он же наименьший, элемент служит в качестве сигнального ключа. Обычно мы будем избегать употребления в коде сигнальных ключей, поскольку часто легче понять код с явными проверками. Но мы будем обязательно отмечать ситуации, когда сигнальные ключи могут оказаться полезными для упрощения программы и повышения ее эффективности.

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

t = a[j]; a[j] = a[j-1]; a[j-1] = t;
      

затем

t = a[j-1]; a[j-1] = a[j-2]; a[j-2] = t
      

и т.д. Значение t между этими двумя последовательностями не изменяется, но происходит бесполезная трата времени на его запоминание и тут же на чтение для следующего обмена. Программа 6.3 сдвигает большие элементы вправо без выполнения обменов, тем самым избегая напрасной траты времени.

Программа 6.3. Сортировка вставками

Это усовершенствованный вариант функции sort из программы 6.1, поскольку он (1) помещает в первую позицию наименьший элемент массива, чтобы использовать его как сигнальный ключ; (2) во внутреннем цикле выполняет лишь одну операцию присваивания, а не обмена; и (3) прекращает выполнение внутреннего цикла, когда вставляемый элемент уже находится в требуемой позиции. Для каждого i упорядочиваются элементы a[l], ..., a[i] с помощью сдвига на одну позицию вправо элементов a[l], ..., a[i-1] из отсортированного списка, которые больше a[i], и занесения a[i] в освободившееся место.

template <class Item>
void insertion(Item a[], int l, int r)
  { int i;
    for (i = r; i > l; i--) compexch(a[i-1], a[i]);
    for (i = l+2; i <= r; i++)
      { int j = i; Item v = a[i];
        while (v < a[j-1])
{ a[j] = a[j-1]; j--; }
        a[j] = v;
      }
  }
      

Реализация сортировки вставками в программе 6.3 значительно эффективнее реализации в программе 6.1 (в разделе 6.5 мы увидим, что она работает почти вдвое быстрее). В данной книге нас интересуют как элегантные и эффективные алгоритмы, так и элегантные и эффективные реализации этих алгоритмов. В данном случае алгоритмы несколько отличаются друг от друга — правильнее назвать функцию sort из программы 6.1 неадаптивной (nonadaptive) сортировкой вставками. Глубокое понимание свойств алгоритма — лучшее руководство для разработки его реализации, которая может эффективно использоваться в различных приложениях.

В отличие от сортировки выбором, время выполнения сортировки вставками зависит главным образом от исходного порядка ключей во входных данных. Например, если файл большой, а ключи уже упорядочены (или почти упорядочены), то сортировка вставками выполняется быстро, а сортировка выбором — медленно. Более полное сравнение алгоритмов сортировки приведено в разделе 6.5.

Упражнения

6.15. Покажите в стиле рис. 6.3 процесс упорядочения файла E A S Y Q U E S T I O N методом вставок.

6.16. Приведите реализацию сортировки вставками, в которой внутренний цикл оформлен с помощью оператора while с завершением по одному из двух условий, описанных в тексте.

6.17. Для каждого из условий цикла while в упражнении 6.16 приведите пример файла из N элементов, для которого в момент выхода из цикла это условие всегда ложно.

о 6.18. Является ли сортировка вставками устойчивой?

6.19. Приведите неадаптивную реализацию сортировки выбором, основанную на поиске минимального элемента с помощью кода наподобие первого цикла for из программы 6.3.

Александра Боброва
Александра Боброва

Я прошла все лекции на 100%.

Но в https://www.intuit.ru/intuituser/study/diplomas ничего нет.

Что делать? Как получить сертификат?

Никита Андриянов
Никита Андриянов
Владимир Хаванских
Владимир Хаванских
Россия, Москва, Высшая школа экономики
Вадим Рычков
Вадим Рычков
Россия, Москва, МГТУ Станкин