- •А. С. Третьяков алгоритмы
- •Структуры данных Оглавление
- •2.1 Введение 20
- •4.1 Введение 69
- •5.1 Введение 87
- •Предисловие
- •Раздел I структуры данных
- •Глава 1. Массивы
- •1.1 Введение
- •1.2.1 Метод деления
- •1.2.2 Метод умножения
- •1.3.1 Открытое хеширование
- •1.3.2 Закрытое хеширование
- •1.4 Ассоциативный массив
- •1.5 Динамический массив
- •Глава 2. Списки
- •2.1 Введение
- •2.2 Связный список
- •2.3 Стек
- •2.4 Очередь
- •2.4.1 Реализация очереди с помощью массива
- •2.4.2 Реализация очереди с помощью указателей
- •Глава 3. Графы
- •3.1 Основные понятия и виды графов
- •3.2 Матрица смежности
- •3.3 Список смежности
- •3.4 Список ребер
- •3.5 Матрица инцидентности
- •Глава 4. Деревья
- •4.1 Введение
- •4.2 Двоичное дерево
- •4.3 Двоичное дерево поиска
- •4.4 Куча
- •4.5.1 Балансировка
- •4.5.2 Добавление узлов
- •4.5.3 Удаление узлов
- •Раздел II алгоритмы
- •Глава 5. Анализ сложности алгоритмов
- •5.1 Введение
- •5.2 Асимптотический анализ
- •Глава 6. Сортировка
- •6.1 Сортировка пузырьком
- •6.2 Сортировка выбором
- •6.3 Сортировка вставками
- •6.4 Сортировка Шелла
- •6.5 Быстрая сортировка
- •6.5.1 Разбиение массива
- •6.5.2 Рекурсивная сортировка частей массива
- •6.6 Сортировка слиянием
- •6.7 Гномья сортировка
- •6.8 Шейкерная сортировка
- •Глава 7. Поиск
- •7.1 Линейный поиск
- •7.2 Двоичный поиск
- •7.3 Интерполяционный поиск
- •Глава 8. Теория чисел
- •8.1 Алгоритм Евклида
- •8.1.1 Метод вычитания
- •8.1.2 Метод деления
- •8.2 Бинарный алгоритм вычисления нод
- •8.3 Быстрое возведение в степень
- •8.4 Решето Эратосфена
- •8.5 Решето Сундарама
- •Глава. 9 Графы
- •9.1 Поиск в ширину
- •9.2 Поиск в глубину
- •9.3 Алгоритм Дейкстры
- •9.4 Алгоритм Флойда – Уоршелла
- •9.5 Алгоритм Беллмана — Форда
5.2 Асимптотический анализ
Анализ сравнения затрат времени алгоритмов, выполняемых решение экземпляра некоторой задачи, при больших объемах входных данных, называется асимптотическим. Алгоритм, имеющий меньшую асимптотическую сложность, является наиболее эффективным.
В асимптотическом анализе, сложность алгоритма – это функция, позволяющая определить, как быстро увеличивается время работы алгоритма с увеличением объёма данных.
Основные оценки роста, встречающиеся в асимптотическом анализе:
Ο (О-большое) – верхняя асимптотическая оценка роста временной функции;
Ω (Омега) – нижняя асимптотическая оценка роста временной функции;
Θ (Тета) – нижняя и верхняя асимптотические оценки роста временной функции.
Пусть n – величина объема данных. Тогда рост функции алгоритма f(n) можно ограничить функций g(n) асимптотически:
Обозначение |
Описание |
f(n) Ο(g(n)) |
f ограничена сверху функцией g с точностью до постоянного множителя |
f(n) Ω(g(n)) |
f ограничена снизу функцией g с точностью до постоянного множителя |
f(n) Θ(g(n)) |
f ограничена снизу и сверху функцией g |
Например, время уборки помещения линейно зависит от площади этого самого помещения (Θ(S)), т. е. с ростом площади в n раз, время уборки увеличиться также в n раз. Поиск имени в телефонной книге потребует линейного времени Ο(n), если воспользоваться алгоритмом линейного поиска (см. п. 6.1), либо времени, логарифмически зависящего от числа записей (Ο(log2(n))), в случае применения двоичного поиска (см. п. 6.2).
Для нас наибольший интерес представляет Ο-функция. Кроме того, в последующих главах, сложность алгоритмов будет даваться только для верхней асимптотической границы.
Под фразой «сложность алгоритма есть Ο(f(n))» подразумевается, что с увеличением объема входных данных n, время работы алгоритма будет возрастать не быстрее, чем некоторая константа, умноженная на f(n).
Важные правила асимптотического анализа:
O(k*f) = O(f) – постоянный множитель k (константа) отбрасывается, поскольку с ростом объема данных, его смысл теряется, например:
O(9,1n) = O(n)
O(f*g) = O(f)*O(g) – оценка сложности произведения двух функций равна произведению их сложностей, например:
O(5n*n) = O(5n)*O(n) = O(n)*O(n) = O(n*n) = O(n2)
O(f/g)=O(f)/O(g) – оценка сложности частного двух функций равна частному их сложностей, например:
O(5n/n) = O(5n)/O(n) = O(n)/O(n) = O(n/n) = O(1)
O(f+g) равна доминанте O(f) и O(g) – оценка сложности суммы функций определяется как оценка сложности доминанты первого и второго слагаемых, например:
O(n5+n10) = O(n10)
Подсчет количества операций – дело утомительное и, что важно, совсем не обязательное. Исходя из выше перечисленных правил, чтобы определить сложность алгоритма, не нужно, как мы это делали прежде, считать все операции, достаточно знать какой сложностью обладает та или иная конструкция алгоритма (оператор или группа операторов). Так, алгоритм, не содержащий циклов и рекурсий, имеет константную сложность O(1). Сложность цикла, выполняющего n итераций, равна O(n). Конструкция их двух вложенных циклов, зависящих от одной и той же переменной n, имеет квадратичную сложность O(n2).
Вот наиболее часто встречающиеся классы сложности:
O(1) – константная сложность;
О(n) – линейная сложность;
О(na) – полиномиальная сложность;
О(log(n)) – логарифмическая сложность;
O(n*log(n)) – квазилинейная сложность;
O(2n) – экспоненциальная сложность;
O(n!) – факториальная сложность.
