Сортировка
4.3. Применения сортировки.
4.3.1.
Найти количество различных чисел среди элементов данного
массива. Число действий порядка . (Эта задача
уже была в
"Переменные, выражения, присваивания"
.)
Решение. Отсортировать числа, а затем посчитать количество различных, просматривая элементы массива по порядку.
4.3.2.
Дано n отрезков на прямой
(
). Найти максимальное k, для
которого существует точка прямой, покрытая k отрезками
("максимальное число слоев"). Число действий - порядка
.
Решение. Упорядочим все левые и правые концы отрезков вместе (при этом левый конец считается меньше правого конца, расположенного в той же точке прямой). Далее двигаемся слева направо, считая число слоев. Встреченный левый конец увеличивает число слоев на 1, правый - уменьшает. Отметим, что примыкающие друг к другу отрезки обрабатываются правильно: сначала идет левый конец (правого отрезка), а затем - правый (левого отрезка).
4.3.3.
Дано точек на плоскости. Указать
-звенную
несамопересекающуюся незамкнутую ломаную, проходящую через
все эти точки. (Соседним отрезкам ломаной разрешается
лежать на одной прямой.) Число действий порядка
.
Решение. Упорядочим точки по -координате, а при
равных
-координатах - по
-координате. В таком
порядке и можно проводить ломаную.
4.3.4. Та же задача, если ломаная должна быть замкнутой.
Решение. Возьмем самую левую точку (то есть точку
с наименьшей -координатой) и проведем из нее лучи во
все остальные точки. Теперь упорядочим эти лучи снизу
вверх, а точки на одном луче упорядочим по расстоянию от
начала луча (это делается для всех лучей, кроме нижнего
и верхнего). Ломаная выходит из выбранной (самой левой)
точки по нижнему лучу, затем по всем остальным лучам
(в описанном порядке) и возвращается по верхнему лучу.
4.3.5.
Дано точек на плоскости. Построить их выпуклую
оболочку - минимальную выпуклую фигуру, их содержащую.
(Резиновое колечко, натянутое на вбитые в доску гвозди - их
выпуклая оболочка.) Число операций не более
.
Указание. Упорядочим точки - годится любой из порядков, использованных в двух предыдущих задачах. Затем, рассматривая точки по очереди, будем строить выпуклую оболочку уже рассмотренных точек. (Для хранения выпуклой оболочки полезно использовать дек, см. "Типы данных" . Впрочем, при упорядочении точек по углам это излишне.)
4.4. Нижние оценки для числа сравнений при сортировке
Пусть имеется различных по весу камней и весы, которые
позволяют за одно взвешивание определить, какой из двух
выбранных нами камней тяжелее. (В программистских терминах:
мы имеем доступ к функции тяжелее(i,j:1..n):boolean.)
Надо упорядочить камни по весу, сделав как можно меньше
взвешиваний (вызовов функции тяжелее ).
Разумеется, число взвешиваний зависит не только от выбранного нами алгоритма, но и от того, как оказались расположены камни. Сложностью алгоритма назовем число взвешиваний при наихудшем расположении камней.
4.4.1.
Доказать, что сложность произвольного алгоритма сортировки камней не меньше
! (где
).
Решение. Пусть имеется алгоритм сложности не более .
Для каждого из
! возможных расположений камней
запротоколируем результаты взвешиваний (обращений к функции тяжелее ); их можно записать в виде последовательности
из не более чем
нулей и единиц. Для единообразия
дополним последовательность нулями, чтобы ее длина стала
равной
. Тем самым у нас имеется
!
последовательностей из
нулей и единиц. Все эти
последовательности разные - иначе наш алгоритм дал бы
одинаковые ответы для разных порядков (и один из ответов
был бы неправильным). Получаем, что
! - что
и требовалось доказать.
Другой способ объяснить то же самое - рассмотреть дерево
вариантов, возникающее в ходе выполнения алгоритма,
и сослаться на то, что дерево высоты не может иметь
более
листьев.
Несложно заметить, что при
подходящем
, поскольку в сумме


Тем самым любой алгоритм сортировки, использующий только
сравнения элементов массива и их перестановки, требует не
менее действий, так что наши алгоритмы близки
к оптимальным. Однако алгоритм сортировки, использующий
другие операции, может действовать и быстрее. Вот один из
примеров.
4.4.2.
Имеется массив целых чисел , причем
все числа неотрицательны и не превосходят m.
Отсортировать этот массив; число действий порядка
.
Решение. Для каждого числа от 0 до m подсчитываем, сколько раз оно встречается в массиве. После этого исходный массив можно стереть и заполнить заново в порядке возрастания, используя сведения о кратности каждого числа.
Отметим, что этот алгоритм не переставляет числа в массиве, как большинство других, а "записывает их туда заново".
Есть также метод сортировки, в котором последовательно проводится ряд "частичных сортировок" по отдельным битам. Начнем с такой задачи.
4.4.3.
В массиве целых чисел переставить
элементы так, чтобы четные числа шли перед нечетными (не
меняя взаимный порядок в каждой из групп).
Решение. Сначала спишем (во вспомогательный массив) все четные, а потом - все нечетные.
4.4.4.
Имеется массив из чисел от
до
, каждое из
которых мы будем рассматривать как
-битовое слово из
нулей и единиц. Используя проверки "
-ый бит
равен
" и "
-ый бит
равен
" вместо
сравнений, отсортировать все числа за время
порядка
.
Решение. Отсортируем числа по последнему биту
(см. предыдущую задачу), затем по предпоследнему и так
далее. В результате они будут отсортированы. В самом деле,
индукцией по легко доказать, что после
шагов
любые
два числа, отличающиеся только в
последних битах, идут
в правильном порядке. (Вариант: после
шагов
-битовые концы чисел идут в правильном порядке.)
Аналогичный алгоритм может быть применен для -ичной
системы счисления вместо двоичной. При этом полезна такая
вспомогательная задача:
4.4.5.
Даны чисел и функция
, принимающая (на них)
значения
. Требуется переставить числа в таком
порядке, чтобы значения функции
не убывали (сохраняя
порядок для чисел с равными значениями
). Число действий
порядка
.
Указание.
Завести списков суммарной длины
(как это сделать,
смотри в
"Типы данных"
о типах данных)
и помещать
в
-ый список числа, для которых значение
функции
равно
. Вариант: посчитать для всех
, сколько
имеется
чисел
с
, после чего легко определить,
с какого
места нужно начинать размещать числа
с
.
4.4.6.
Даны целых чисел в диапазоне от
до
. Как
отсортировать их, сделав порядка
действий?