- •Алгоритмы и алгоритмические языки
- •Лекция 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 без сжатия.
-
Лекция 14
-
Поиск строк
Пусть имеется последовательность символов S={ si } из алфавита : si , i=1,…,N и последовательность W={ wi } из алфавита : wi , i=1,…,M, MN.
Ставится задача поиска всех таких целых 0kN-M, что для всех i=1,…,M: sk+i=wi .
Стандартной интерпретацией данной задачи является задача поиска заданного слова в строке или задача поиска слова в файле.
У данной задачи существует прямое решение, при котором происходит последовательная проверка совпадения подстроки W со всеми подряд идущими подстроками строки S длины M. Легко увидеть, что данный алгоритм требует времени порядка (MN) (реализация данного алгоритма приведена в следующем параграфе). На самом деле задачу можно решить существенно быстрее, о чем и пойдет речь далее.
-
Отступление на тему языка с. Ввод-вывод строк из файла
Стандартной интерпретацией поставленной задачи является задача поиска заданного слова в текстовом файле. В ОС UNIX имеется стандартная программа grep поиска слов по шаблону в текстовых файлах. Ее простейший формат вызова следующий:
grep шаблон список_файлов_поиска
здесь вместо слова шаблон можно подставить просто слово, которое требуется найти в тексте файлов из списка список_файлов_поиска. Имена файлов в списке разделяются пробелом. Если список имен файлов пуст, то слово ищется в стандартном потоке ввода.
Следующая программа демонстрирует, как можно простейшим способом реализовать функцию grep для случая, когда строки в файлах имеют длину не более 512 символов и когда вместо шаблона поиска вводится простое слово.
#include <stdio.h>
#include <string.h>
int main(int npar,char **par)
{FILE *f; int i,istr; char str[512]; if(npar<=1)return -1;
for(i=(npar==2?1:2);i<npar;i++)
{
f=(npar==2?stdin:fopen(par[i],"r"));
if(f)
{
for(istr=1;fgets(str,512,f);istr++)
if(strstr(str,par[1]))
{printf("%s: %d: %s",par[i],istr,str);}
fclose(f);
}
}
return 0;
}
Программа демонстрирует следующие возможности:
-
Передачу параметров из командной строки
-
Открытий/закрытие файлов
-
Ввод текста из файла
-
Использование стандартного потока ввода
-
Стандартную процедуру поиска слова в тексте
Детальное описание всех указанных возможностей следует искать в документации к языку С.
-
Алгоритм поиска подстроки с использованием хеш-функции (Алгоритм Рабина-Карпа)
Идея алгоритма проста: для каждой подстроки Si строки S, используемой при сравнении c W (т.е. подстроки длины, равной длине W), вычисляется значение некоторой хэш-функции h(Si). Если h(Si)= h(S), то данная подстрока является хорошим претендентом на равенство и для нее производится полное сравнение, иначе переходим к следующей подстроке Si+1. При вычислении h(Si) мы можем использовать тот факт, что строка Si отличается всего на два символа от строки Si-1, поэтому есть шанс использовать уже вычисленное значение h(Si-1) для вычисления h(Si). Действительно, это можно сделать, если в качестве хэш-функции использовать остаток от деления на некоторое число K. При этом строка должна интерпретироваться как одно большое целое число. Действительно
Si=(si+0,…, si+M-1) =( si-1, si+0,…, si+M-1)-256M si-1=
=256( si-1, si+0,… , si+M-2) + si+M-1 - 256M si-1
из чего вытекает
Si%K = ( 256 (( si-1, si+0,… , si+M-2) %K) + si+M-1 – (256M%K) si-1 ) %K
h(Si) = ( 256 h(Si-1) + si+M-1 – (256M%K) si-1 ) %K
Единственное большое число, возникающее в последней формуле, это 256M, поэтому (256M%K) следует вычислить заранее. Наконец, если K выбрать таким образом, чтобы 256K<231-257, то (256M%K) si-1<231-257 , 256 h(Si-1) <231-257 и тогда все вычисления могут производиться в рамках обычных целых чисел. Действительно
|256 h(Si-1) + si+M-1 – (256M%K) si-1 |
MAX(|256 h(Si-1) |,|(256M%K) si-1|)+ si+M-1
231-1
что помещается в переменную int.
Осталось заметить, что K должно быть простым числом. В качестве K можно взять K =8388593. Действительно
256K =2147479808
231-257 =2147483391
При идеальном распределении значений хэш-функции каждое ее значение будет появляться с вероятностью 1/K, поэтому время работы алгоритма для неудачного поиска будет складываться из времени предварительных вычислений (M), времени поиска при отсутствии коллизий (N) и времени поиска при наличии коллизий (MN/K). Полное время поиска при наличии в строке S n вхождений строки W будет следующим
T=(M+N+MN/K)+ (Mn)
Итак, мы доказали следующую теорему
Теорема. При идеальном распределении значений хэш-функции в среднем алгоритм Рабина-Карпа требует времени
T=(M+N+MN/K)+ (Mn)
где M – длина искомой подстроки, N – длина строки входных данных, n – количество вхождений искомой строки в строку входных данных, K – модуль, используемый при вычислении остатка от деления в хэш-функции.
В худшем случае алгоритм работает за время
T=(MN).