Алгоритмы на графах. Реализация в библиотеке stl
DESCRIPTION
Алгоритмы на графах. Реализация в библиотеке STL. Выполнила Кулагина С.О. определения. Будем рассматривать задачу в терминах ориентированных графов. - PowerPoint PPT PresentationTRANSCRIPT
Выполнила Кулагина С.О.
ОПРЕДЕЛЕНИЯ Будем рассматривать задачу в терминах ориентированных графов. Граф – конечное множество V, называемое множеством вершин, на
котором задано симметричное антирефлексивное отношение R и выделено множество E двухэлементных подмножеств V, определяемое как {a,b}€ E тогда и только тогда, когда (a,b) € R и a ≠ b, и (b,a) не входит в Е.
Вес – параметр дуги, длина дуги. Путь – сумма длин входящих в него дуг. Полустепенью исхода(захода) называется число исходящих
(входящих) дуг. Две вершины – смежные, если они соединены дугой. Длина пути(во взвешенном графе) – ∑ весов дуг графа. Цикл – путь, который начинается и заканчивается в одной той же
вершине и содержит как минимум 1 дугу. Ациклическим считается граф, не содержащий ни одного цикла. В разреженном графе m<<n2, где m – число дуг, n – число
вершин. Иначе , m=c*n, где с – константа, с≤4. В насыщенном графе m≈n2 или
m ≈ c*n2 , где с – константа, с<1.
СТРУКТУРА БИБЛИОТЕКИ STL
Библиотека содержит пять основных видов компонентов:
* алгоритм (algorithm): определяет вычислительную процедуру. * контейнер (container): управляет набором объектов в памяти. * итератор (iterator): обеспечивает для алгоритма средство доступа к содержимому контейнера. * функциональный объект (function object): инкапсулирует функцию в объекте для использования другими компонентами. * адаптер (adaptor): адаптирует компонент для обеспечения различного интерфейса.
3 ВОЗМОЖНЫХ ПРЕДСТАВЛЕНИЯ
• Матрица смежности(vector<vector<int> >) – для насыщенного графа
• Структура смежности(vector< list<int> > )– Для разреженного графа
• Представление в форме map– Для представления при входе
ИСПОЛЬЗУЕМЫЕ КЛАССЫ БИБЛИОТЕКИ.
Для ввода-вывода
Для представления графа в виде матрицы
Для создания списков
Для определения функторов
Для создания очередей(например, для хранения вершин)
Для создания векторов- структур, аналогичных массивам, но имеющих большую функциональность
Для создания строк
ПРЕДСТАВЛЕНИЕ ГРАФА: ВАРИАНТ1 Если граф взвешенный:
Представление графа в виде массива дуг с помощью STL map.
Дуге(из 2-х вершин) сопоставляется её вес.
Пример: представление визуальное и в форме map
Пример: map<string, list<int> >::iterator it; // Создание итератора для перехода по mapInt i =10;it->first.push_back(i);// первая вершина
Используем это представление для упорядочения графа на входе в программу
ПРЕДСТАВЛЕНИЕ ГРАФА: ВАРИАНТ 2
Если граф насыщенный: Используется двумерная матрица смежности A. Для дуги (vi,wj) с весом ci,j
A[i][j] = ci,j
Несуществующие дуги – значение «бесконечность»
В STL представляется как vector<vector<int> >
ПРЕДСТАВЛЕНИЕ ГРАФА : ВАРИАНТ 3 Если граф разреженный и связный:
Используется структура смежности: массив списков. vector< list<int> >
Хранится вектор вершин, которым сопоставлены списки номеров вершин, смежных с ними.
№ в
ерш
ины
Спи
сок
смеж
ных
верш
ин
Вес соответствующей дуги
vector<list<int>>::iterator it = new interator;
Будем представлять граф именно так.
ОБХОД В ШИРИНУoАлгоритм:
Выбираем непройденную вершину.Проверяем все рёбра и запоминаем непройденные вершины на их концах.Выбираем непройденную вершину и повторяем действия.
ОБХОД В ШИРИНУ:КОДtypedef pair<int, bool> vertex;
typedef multimap<int, int>::iterator edgesIt;
vector<vertex> vertices;
multimap<int, int> edges;
deque<vertex> q;
while(1)
{ current = q.front();
q.pop_front();
vector<vertex>::iterator vIt = find(vertices.begin(), vertices.end(), vertex(current.first, current.second));
(*vIt).second = true;
cout << "Visited: " << (*vIt).first << endl;
pair<edgesIt, edgesIt> rangeIt = edges.equal_range(current.first);
for(edgesIt eIt = rangeIt.first; eIt != rangeIt.second; eIt++)
{ if((find(vertices.begin(), vertices.end(), vertex((*eIt).second, true)) == vertices.end()) && (find(q.begin(), q.end(), vertex((*eIt).second, false)) == q.end())) {push_back(vertex((*eIt).second, false)); cout << "To deque: " << (*eIt).second << endl;
} }
if(q.empty()) break;
} cout << "End." << endl;
return EXIT_SUCCESS;}
Текущая вершина – первая в очереди кандидатов
Вершина представлена парой значений: номер и посещение
Очередь реализована как deque
Проверка непустоты очереди
Выход из цикла, если все вершины просмотрены
ОБХОД В ГЛУБИНУ Алгоритм: Выбираем непройденную
вершину Выбираем 1 из непройденных
смежных вершин Если нет смежных
непройденных вершин, то помечаем вершину пройденной и возвращаемся в запомненную вершину.
ОБХОД В ГЛУБИНУ:КОД Рекурсивная реализация:
vector < vector<int> > g; // граф int n; // число вершин vector<char> used; void dfs (int v) { used[v] = true; for (vector<int>::iterator i=g[v].begin(); i!=g[v].end(); ++i) if (!used[*i]) dfs (*i); }
Рекурсивный вызов
АЛГОРИТМ ДЕЙКСТРЫ
В ориентированном графе с заданными длинами рёбер найти кратчайшие пути из одной вершины во все остальные.
ЗАДАЧА
ОПРЕДЕЛЕНИЕ АЛГОРИТМА
Алгоритм: Если – длина дуги (i,j).
Шаг 0. Помечаем нулевую вершину индексом 0 Шаг i. Помечаем вершину i индексом λi = min(λi+ λij).
НАЧАЛЬНЫЕ СТАДИИ АЛГОРИТМА
Придаём всем вершинам бесконечные весаОтмечаем вершину - начало
Первый по очереди сосед вершины 1 — вершина 2, потому что длина пути до нее минимальна. Длина пути в нее через вершину 1 равна кратчайшему расстоянию до вершины 1 + длина ребра, идущего из 1 в 2, то есть 0 + 7 = 7. Это меньше текущей метки вершины 2, поэтому новая метка 2-й вершины равна 7.
Аналогичную операцию проделываем с двумя другими соседями 1-й вершины — 3-й и 6-й.
Все соседи вершины 1 проверены. Текущее минимальное расстояние до вершины 1 считается окончательным и обсуждению не подлежит (то, что это действительно так, впервые доказал Дейкстра). Вычеркнем её из графа, чтобы отметить, что эта вершина посещена. Выберем следующую вершину
Снова пытаемся уменьшить метки соседей выбранной вершины, пытаясь пройти в них через 2-ю. Соседями вершины 2 являются 1, 3, 4.
Первый (по порядку) сосед вершины 2 — вершина 1. Но она уже посещена, поэтому с 1-й вершиной ничего не делаем.
Следующий сосед вершины 2 — вершина 4. Если идти в неё через 2-ю, то длина такого пути будет = кратчайшее расстояние до 2 + расстояние между вершинами 2 и 4 = 7 + 15 = 22. Поскольку 22<, устанавливаем метку вершины 4 равной 22.
ПОВТОРЯЕМ АНАЛОГИЧНЫЕ ДЕЙСТВИЯ ДЛЯ ОСТАЛЬНЫХ ВЕРШИН
КОД priority_queue<int, vector<int>, Cheapest> candidates; while(1) { for(list<int>::iterator p = outgoing_services[from].begin(); p!
=outgoing_services[from].end();p++){ int b = cities[from]->total_distance+(*p)->distance; int c = cities[from]->from_city; if(cities[from]->visited==false){ if(cities[(*p)->destination]->total_fee==0) { cities[(*p)->destination]->total_fee = a; cities[(*p)->destination]->total_distance = b; cities[(*p)->destination]->from_city = c; } else if(cities[(*p)->destination]->total_fee>a)
{ cities[(*p)->destination]->total_fee=a; cities[(*p)->destination]->total_distance = b; cities[(*p)->destination]->from_city = from; } candidates.push(cities[(*p)->destination]); }} cities[from]->visited = true; if (cities[to]->visited) { return pair<int,int>(cities[to]->total_fee, cities[to]->total_distance);}
Else { return pair<int,int>(INT_MAX, INT_MAX); } }
Очередь с приоритетом для кандидатов
Перебор по спискам смежности Присвоение
min значений длины пути
Для непосещённых вершин
Добавляем вершину – «кандидата»
АЛГОРИТМ БЕЛЛМАНА-ФОРДА Алгоритм Дейкстры - только в графе с
положительными весами дуг. Для графа с отрицательными весами :
как поступать с циклом с отрицательным весом? он делает большинство путей неопределными.
Так, путь от V2 до V5 не определён, потому что можно попасть в петлю V3-V4-V1.
Эту проблему решает алгоритм Беллмана-Форда
РЕАЛИЗАЦИЯ АЛГОРИТМА БЕЛЛМАНА-ФОРДА
Аналогична реализации алгоритма Дейкстры Отличие:
If (элемент есть в очереди) then она не включается повторно.
КОД.
If(find(candidates.begin(), candidates.end(), cities[(*p)->destination)==candidates.end())
candidates.push(cities[(*p)->destination]
Если очередь не содержит кандидата, то он добавляется
АЦИКЛИЧЕСКИЕ ГРАФЫ Задача поиска кратчайшего пути связана с
топологической сортировкой. Топологическая сортировка упорядочивает
вершины в направленном ациклическом графе так, что If (имеется путь из u в v) then {v появляется после u в очерёдности.}
Она невозможна, если граф имеет хотя бы 1 цикл. Описание:
Находится вершина, не имеющая входных дуг. Она и все исходящие из неё дуги удаляются из графа.
АЛГОРИТМ ТОПОЛОГИЧЕСКОЙ СОРТИРОВКИ
Все непройденные вершины с полустепенью захода 0 добавляются в очередь.
Чтобы получить следующую вершину, мы извлекаем первый элемент из очереди.
Когда полустепень захода элемента понижается до 0, он помещается в очередь
КОД.vector < vector<int> > g; // граф
int n; // число вершин
vector<bool> used;
vector<int> ans;
void dfs (int v) // процедура пометки непройденных вершин
{ used[v] = true;
for (vector<int>::itetator i=g[v].begin(); i!=g[v].end(); ++i)
if (!used[*i]) dfs (*i);
ans.push_back (v); }
void topological_sort (vector<int> & result)// сама сортировка
{ used.assign (n, false);
for (int i=0; i<n; ++i)
if (!used[i]) dfs (i);
result = ans; }
ИСПОЛЬЗОВАННАЯ ЛИТЕРАТУРА: Data Structures and Problem Solving with C++ [2nd Ed] [M.
A. Weiss] [Addison Wesley] Полный справочник по С++, Г.Шилдт http://www.rsdn.ru/ http://e-maxx.ru/algo/ Дж. А. Андерсон, Дискретная математика и
комбинаторика, М:2004.