- •Структуры и алгоритмы обработки данных
- •Алгоритмы. Основные определения
- •От задачи к программе
- •Написание программы.
- •Типы данных, структуры данных и абстрактные типы данных
- •Указатели и курсоры
- •Время выполнения программ
- •Измерение времени выполнения программы
- •Степень (порядок) роста
- •Вычисление времени выполнения программ
- •Линейные абстрактные типы данных атд «Список»
- •Реализация списков посредством массивов
- •Реализация списков с помощью указателей
- •Атд «Стек»
- •Атд «Очередь»
- •Реализация очередей с помощью циклических массивов
- •Нелинейные абстрактные типы данных Деревья
- •Порядок узлов
- •Прямой, обратный и симметричный обходы дерева
- •Помеченные деревья и деревья выражений
- •Представление деревьев с помощью массивов
- •Специальные виды множеств атд “Дерево двоичного поиска”
- •Атд "Словарь", основанный
- •Представление ориентированных графов
- •Атд для ориентированных графов
- •Задача нахождения кратчайшего пути
- •Int **c, // массив стоимостей
- •Int *d) // массив кратчайших
- •Остовные деревья минимальной стоимости
- •Обход графов
- •Поиск в ширину
- •Поиск в глубину
- •Сортировка
- •Простые схемы сортировки Метод «пузырька»
- •Сортировка вставками
- •Быстрая сортировка
- •Пирамидальная сортировка
- •«Карманная» сортировка
- •Порядковые статистики
- •Методы разработки алгоритмов. Типы алгоритмов
- •Алгоритмы «разделяй и властвуй» (метод декомпозиции)
- •Баланс подзадач
- •Динамическое программирование
- •Перемножение нескольких матриц
- •Шаг 1: строение оптимальной расстановки скобок
- •Шаг 2: рекуррентное соотношение
- •Шаг 3: вычисление оптимальной стоимости
- •Void MatrixChainOrder(int n, // кол-во матриц
- •Int p[], // размеры матриц
- •Int **s) // оптимальное k
- •Шаг 4: построение оптимального решения
- •Int **s, // таблица, полученная
- •Int I, // индексы
- •Когда применимо динамическое программирование?
- •Оптимальность для подзадач
- •Перекрывающиеся подзадачи
- •«Жадные» алгоритмы
- •"Жадные" алгоритмы как эвристики
- •Когда применим жадный алгоритм?
- •Принцип жадного выбора
- •Оптимальность для подзадач
- •Поиск с возвратом
- •Функции выигрыша
- •Метод ветвей и границ
- •Структуры данных и алгоритмы для внешней памяти
- •Внешняя сортировка
- •Хранение данных в файлах
- •Простая организация данных
- •Хешированные файлы
- •Индексированные файлы
- •Содержание
- •Глава I. Линейные абстрактные типы данных 31
- •Глава II. Сортировка 108
Поиск в глубину
Поиск в глубину – это обобщение метода обхода дерева в прямом порядке. Стратегия поиска в глубину такова: идти «вглубь» пока это возможно (есть не пройденные исходящие ребра), и возвращаться и искать другой путь, когда таких ребер нет. Так делается, пока не будут обнаружены все вершины, достижимые из исходной. Если после этого остаются необнаруженные вершины, можно выбрать одну из них и повторить процесс. Делать так до тех пор, пока мы не обнаружим все вершины графа.
Как и при поиске в ширину, обнаружив впервые вершину v, смежную с u, мы отмечаем это событие, помещая в поле массива pi[v] значение u. Получается дерево – или несколько деревьев, если поиск повторяется из нескольких вершин. Каждая вершина попадает ровно в одно дерево поиска в глубину, так что эти деревья не пересекаются. Деревья поиска в глубину образуют лес поиска в глубину.
Алгоритм поиска в глубину также использует цвета вершин. Изначально все вершины белые. Будучи обнаруженной, она перекрашивается в серый цвет. А когда список смежных с ней вершин будет полностью просмотрен, она станет черной.
Помимо этого алгоритм ставит на вершинах метки времени. Каждая вершина имеет две метки: в d[v] записано, когда эта вершина была обнаружена и сделана серой, а в f[v] – когда была закончена обработка списка смежных с v вершин и v стала черной. Эти метки используются во многих алгоритмах на графах и полезны для анализа свойств поиска в глубину.
Процедуру, реализующую этот алгоритм, называют DFS(), что является сокращением от depth-first search – поиск в глубину. Time – глобальная переменная текущего времени, используемая для пометок.
1 2 3 4 5 6 7 8 9 10 11 12 13
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
DFS(GRAPH G) { Vertex u; For (для каждой вершины u графа G) { color[u]=WHITE; /*вершины становятся белыми */ pi[u]=NULL; /* родителем всех вершин считается NULL */ } time=0; For (для каждой вершины u графа G) if (color[u]==WHITE) DFS-Visit(u); }
DFS-Visit(vertex u) { vertex v; color[u]=GRAY; d[u]=++time; for(для всех вершин v смежных с u) if (color[v]==WHITE) { pi[v]=u; DFS-Visit(v); } color[u]=BLACK; f[u]=++time; } |
В строках 6-7 все вершины красятся в белый цвет, в поле pi помещается NULL. В строке 9 устанавливается начальное нулевое время. В строках 10-12 вызывается процедура DFS-Visit для всех вершин которые остались белыми к моменту вызова. Эти вершины становятся корнями деревьев поиска в глубину. В момент вызова DFS-Visit(u) вершина u – белая. В строке 4 она становится серой. В строке 5 счетчик времени увеличивается на 1 и полученное время заносится в d[u]. В строках 6-11 просматриваются смежные с u вершины; процедура DFS-Visit вызывается для тех из них, которые оказываются белыми к моменту вызова. После просмотра всех смежных с u вершин мы делаем вершину u черной и записываем в f[u] время этого события.
Анализ
Оценим время работы процедуры поиска в ширину. В процессе работы вершины только темнеют, так что каждая вершина кладется в очередь не более одного раза. Следовательно, вынуть ее можно только один раз. Каждая операция с очередью требует О(1) шагов, так что всего на операции с очередью уходит время О(V). Теперь заметим, что список смежных вершин просматривается лишь когда вершина извлекается из очереди, т.е. не более одного раза. Сумма длин всех этих списков равна Е (количеству ребер ориентированного графа) или 2Е (удвоенному количеству ребер неориентированного графа) и всего на их обработку уйдет время О(Е). Инициализация требует О(V) шагов, так что всего получается О(V+Е)=О(max(V,E)). Поскольку Е>V, то часто считают что О(V+Е)=О(Е). Тем самым время работы процедуры BFS() пропорционально размеру представления графа G в виде списков смежных вершин.
Время выполнения алгоритма поиска в глубину такое же, как и у алгоритма поиска в ширину.