- •Алгоритмы и алгоритмические языки
- •Лекция 1
- •Представление чисел в эвм
- •Вещественные
- •Ошибки вычислений
- •Лекция 2
- •Алгоритмы. Сведение алгоритмов.
- •Нижние и верхние оценки.
- •Сортировки
- •Постановка задачи
- •Сортировка пузырьком.
- •Сортировка слиянием с рекурсией.
- •Сортировка слиянием без рекурсии.
- •Лекция 3
- •Алгоритмы. Сведение алгоритмов.
- •Сортировки и связанные с ними задачи.
- •Доказательство корректности работы алгоритма.
- •Оценки времени работы алгоритма.
- •Некоторые задачи, сводящиеся к сортировке.
- •Лекция 4
- •Алгоритмы. Сведение алгоритмов.
- •Сортировки и связанные с ними задачи.
- •HeapSort или сортировка с помощью пирамиды.
- •Алгоритмы сортировки за время o(n)
- •Сортировка подсчетом
- •Цифровая сортировка
- •Сортировка вычерпыванием
- •Лекция 5
- •Алгоритмы. Сведение алгоритмов.
- •Порядковые статистики.
- •Поиск порядковой статистики за время (n) в среднем
- •Поиск порядковой статистики за время (n) в худшем случае
- •Язык программирования c.
- •Переменные
- •Структуры данных.
- •Вектор.
- •Лекция 6
- •Стек. Реализация 1 (на основе массива).
- •Стек. Реализация 2 (на основе массива с использованием общей структуры).
- •Стек. Реализация 3 (на основе указателей).
- •Стек. Реализация 4 (на основе массива из двух указателей).
- •Стек. Реализация 5 (на основе указателя на указатель).
- •Очередь.
- •Стандартная ссылочная реализация списков
- •Ссылочная реализация списков с фиктивным элементом
- •Реализация l2-списка на основе двух стеков
- •Реализация l2-списка с обеспечением выделения/освобождения памяти
- •Лекция 7
- •Структуры данных. Графы.
- •Поиск пути в графе с наименьшим количеством промежуточных вершин
- •Представление графа в памяти эвм
- •Массив ребер
- •Матрица смежности
- •Матрица инцидентности
- •Списки смежных вершин
- •Реберный список с двойными связями (рсдс) (для плоской укладки планарных графов)
- •Лекция 8
- •Структуры данных. Графы.
- •Поиск кратчайшего пути в графе
- •Алгоритм Дейкстры
- •Конец вечного цикла
- •Алгоритм Дейкстры модифицированный
- •Конец вечного цикла
- •Лекция 9
- •Бинарные деревья поиска
- •Поиск элемента в дереве
- •Добавление элемента в дерево
- •Поиск минимального и максимального элемента в дереве
- •Удаление элемента из дерева
- •Поиск следующего/предыдущего элемента в дереве
- •Слияние двух деревьев
- •Разбиение дерева по разбивающему элементу
- •Сбалансированные и идеально сбалансированные бинарные деревья поиска
- •Операции с идеально сбалансированным деревом
- •Операции со сбалансированным деревом
- •Поиск элемента в дереве
- •Добавление элемента в дерево
- •Удаление элемента из дерева
- •Поиск минимального и максимального элемента в дереве
- •Поиск следующего/предыдущего элемента в дереве
- •Слияние двух деревьев
- •Разбиение дерева по разбивающему элементу
- •Лекция 10
- •Красно-черные деревья
- •Отступление на тему языка с. Поля структур.
- •Отступление на тему языка с. Бинарные операции.
- •Высота красно-черного дерева
- •Добавление элемента в красно-черное дерево
- •Однопроходное добавление элемента в красно-черное дерево
- •Удаление элемента из красно-черного дерева
- •Лекция 11
- •Высота b-дерева
- •Поиск вершины в b-дереве
- •Отступление на тему языка с. Быстрый поиск и сортировка в языке с
- •Добавление вершины в b-дерево
- •Удаление вершины из b-дерева
- •Лекция 12
- •Хеширование
- •Метод многих списков
- •Метод линейных проб
- •Метод цепочек
- •Лекция 14
- •Поиск строк
- •Отступление на тему языка с. Ввод-вывод строк из файла
- •Алгоритм поиска подстроки с использованием хеш-функции (Алгоритм Рабина-Карпа)
- •Конечные автоматы
- •Отступление на тему языка с. Работа со строками
- •Алгоритм поиска подстроки, основанный на конечных автоматах
- •Лекция 15
- •Алгоритм поиска подстроки Кнута-Морриса-Пратта (на основе префикс-функции)
- •Алгоритм поиска подстроки Бойера-Мура (на основе стоп-символов/безопасных суффиксов)
- •Эвристика стоп-символа
- •Эвристика безопасного суффикса
- •Форматы bmp и rle
- •Bmp без сжатия.
-
Оценки времени работы алгоритма.
Оценим временя работы приведенного алгоритма в худшем случае.
Теорема. Время работы алгоритма QuickSort равно O(N 2), где N – количество элементов в сортируемом массиве.
Доказательство. После каждого разбиения массива на две части длина самой большой из двух образовавшихся половин оказывается меньше либо равной длине разбиваемого массива –1. Поэтому на каждой ветви алгоритма будет не более N узлов (разбиений массива). На каждом уровне дерева разбиений присутствуют не более N сортируемых элементов, поэтому время, затрачиваемое на слияние их подмножеств равно O( N ). Итого, суммарное время работы алгоритма равно O( N ) * N = O( N 2).
Данная оценка достижима на массиве {N,N-1,…,1}.
Оказывается, что число ``неприятных’’ случаев, т.е. таких расположений массивов чисел, при которых время работы алгоритма QuickSort велико, оказывается, относительно, небольшим. Вообще, верна теорема
Теорема. Среднее время работы алгоритма QuickSort равно (N log2 N), где N – количество элементов в сортируемом массиве. Под средним временем подразумевается среднее время по всем перестановкам любого массива входных данных длины N, состоящего из различных элементов.
Данная теорема объясняет, в каком смысле данный алгоритм является оптимальным. В то же время, в реальной жизни, часто поток входных данных не является случайным, поэтому в качестве медианы следует брать случайно выбранный элемент. Для этого внесем в алгоритм QuickSort следующее дополнение. Перед присваиванием x=Ai поменяем местами i-ый элемент массива со случайно выбранным элементом среди элементов с индексами от p до q. Назовем получившийся алгоритм QuickSortP. Приведенная теорема верна также и для алгоритма QuickSortP. Докажем ее именно для последнего алгоритма. Будем, кроме того, предполагать, что все элементы входной последовательности различны, или, что то же самое, на входе подается последовательность различных элементов из множества {1,…,N}.
В рассматриваемом случае если x=1, то перед входом в рекурсию алгоритма QuickSort множество разобьется на части размером 1 и N-1. В любом другом случае, как отмечалось выше в доказательстве Замечания 3, элемент x останется в правой половине массива и размер левой половины массива, поэтому, будет равен x-1.
Выпишем рекуррентное соотношение на среднее время работы алгоритма
T(N) = [ (T( 1 ) +T( N-1 )) + i=2iN (T( i-1 ) +T( N-i+1 ))]/N + (N) =
= [ (T( 1 ) +T( N-1 )) + i=1i<N (T( i ) +T( N-i ))]/N + (N) =
= [ (T( 1 ) +T( N-1 ) ]/N + [ i=1i<N (T( i ) +T( N-i ))]/N + (N) =
= [ i=1i<N (T( i ) +T( N-i ))]/N + (N)
Предположим, для i<N верно:
T( i )<a i log i +c для некоторых a>0, c>0 ,
тогда задача сводится к нахождению таких a>0, c>0 , что для них всегда бы выполнялось соотношение
[ i=1i<N (T( i ) +T( N-i ))]/N + (N) < a N log N +c
Итак
[ i=1i<N (T( i ) +T( N-i ))]/N < [ i=1i<N (a i log i +c + a (N-i) log (N-i) +c ))]/N =
= [ i=1i<N (a i log i +c ))]2/N < a [ i=1i<N i log i ]2/N +2 c
Оценим сумму из соотношения:
i=1i<N i log i = i=1i<N i log N + i=1i<N i log (i/N) = N 2 log N / 2 - i=1i<N i log (N/i) N 2 log N / 2 - i=1i<N/4 i log (N/i) N 2 log N / 2 - i=1i<N/4 i 2 N 2 log N / 2 - N 2/ 8
Т.о. имеем
[ i=1i<N (T( i ) +T( N-i ))]/N + (N) < a (N log N - N / 4) + 2 c + (N) =
= a N log N + c + ((N) + c – a N / 4 )
Осталось взять такое большое a, что ((N) + c – a N / 4 )<0, после чего мы получаем требуемое соотношение.
К сожалению, обе приведенные реализации алгоритма QuickSort не являются жизнеспособными. Это связано с тем, что в обоих алгоритмах максимально возможная глубина рекурсии равна N. В этом случае требуется порядка O(N) байт дополнительной памяти, что не фатально, но проблема в том, что эта память будет выделяться в стеке задачи, а стек задачи всегда имеет маленький (относительно) размер. Поэтому, например, в Microsoft Visual Studio мы может ожидать ситуации stack overflow при размерах целочисленного массива порядка 100000 (размер стека здесь по умолчанию равен 1M).
Выходом из положения является следующее решение. Будем рекурсивно решать задачу сортировки только для меньшей половины массива. А большую половину будем сортировать в этой же процедуре. В таком случае глубина погружения в рекурсию не будет превосходить ]log2N[, что является приемлемым.
В реальности для данной реализации требуется внести минимальные добавки в исходный алгоритм быстрой сортировки. Например, алгоритм QuickSort может быть модифицирован следующим образом:
QuickSortR1(A,p,q)
Метка:
Если q-p < 1 то ВЫЙТИ
Вечный цикл
i=p; j=q; x=Ai
Пока A i < x : i + +;
Пока A j > x : j - -;
Если i < j то
поменять местами A i и A j ;
иначе
{
if(j-p<q-(j+1))
{
QuickSort(A, p, j );
p=j+1;q=q;goto Метка;
}
иначе
{
QuickSort(A, j+1, q );
p=p;q=j;goto Метка;
}
ВЫЙТИ
}
i + +; j - -;
Конец вечного цикла
Здесь добавлены:
- метка (может быть заменена еще одним вечным циклом),
- проверка, какая часть массива больше,
- переназначение p,q в каждом случае.