- •Представление графов. Матрица смежности
- •Представление графов. Матрица инцидентности
- •Представление графов. Списки смежности
- •Представление графов. Список ребер
- •Обходы графов. Рекурсивная процедура
- •Обходы графов. Рекурсивная процедура
- •Топологическая сортировка вершин ориентированного графа без циклов. DAG – Directed Acyclic Graph –
- •Топологическая сортировка вершин ориентированного графа без циклов.
- •Топологическая сортировка вершин ориентированного графа без циклов.
- •Обходы графов. Общая процедура с использованием структуры хранения вершин
- •Использование стека для обхода графа.
- •Использование очереди для обхода графа.
- •Поиск кратчайших путей в ненагруженном графе.
- •Программа обхода графа с использованием контейнера.
- •Программа поиска кратчайших путей в ненагруженном графе.
- •Алгоритмы поиска кратчайших путей в нагруженном графе.
- •Алгоритм релаксации ребра при поиске кратчайших путей.
- •Алгоритм Дейкстры поиска кратчайших путей.
- •Программа для реализации алгоритма Дейкстры поиска кратчайших путей.
- •Кратчайшие пути в ориентированном графе
- •Кратчайшие пути в ориентированном графе
- •Транзитивное замыкание графа отношения.
- •Транзитивное замыкание графа отношения. Алгоритм «умножения матриц».
- •Транзитивное замыкание графа отношения. Алгоритм «умножения матриц».
- •Транзитивное замыкание графа отношения. Алгоритм Флойда – Уоршалла
- •Транзитивное замыкание графа отношения. Алгоритм Флойда – Уоршалла
- •Применение алгоритма Флойда – Уоршалла для поиска кратчайших путей
- •Применение алгоритма Флойда – Уоршалла для поиска кратчайших путей
- •Построение минимального скелета нагруженного графа. Алгоритм Прима.
Использование стека для обхода графа.
Граф: |
2 |
|
Если в качестве промежуточной структуры |
|||||||
|
|
хранения при обходе использовать стек, то |
||||||||
1 |
|
3 |
получается обход в глубину. |
|||||||
|
1 |
5 |
8 |
7 |
6 |
4 |
3 |
2 |
||
|
4 |
|
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Можно также получить дерево обхода |
|||||||
5 |
|
6 |
в глубину, если отмечать каждую прямую |
|||||||
|
|
|
или обратную дугу. |
|
|
|
||||
|
7 |
|
|
|
|
|
|
|
|
|
8 |
|
|
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
Стек: |
|
|
3 |
4 |
6 |
1 |
7 |
5 |
5 |
|
|
|
|
||||||||
|
2 |
|
|
|
|
|
|
|
|
|
|
4 |
|
|
|
|
|
|
|
|
|
|
7 |
|
|
|
|
|
|
|
|
|
|
8 |
|
|
|
|
|
|
|
|
|
Использование очереди для обхода графа.
Граф: |
2 |
|
Если в качестве промежуточной структуры |
||||||||
|
|
хранения при обходе использовать очередь, |
|||||||||
1 |
|
3 |
то получается обход в ширину. |
|
|||||||
|
1 |
2 |
4 |
|
5 |
3 |
6 |
7 |
8 |
||
|
4 |
|
|
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Можно также получить дерево обхода в ширину, |
||||||||
5 |
|
6 |
если отмечать каждую прямую дугу. |
||||||||
|
7 |
|
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
|
1 2 1 1 4
5
5
8 Очередь:
2
4
7
8
Поиск кратчайших путей в ненагруженном графе.
Граф: |
|
Алгоритм обхода графа с помощью очереди |
||||||||||
2 |
позволяет построить дерево кратчайших путей |
|||||||||||
|
||||||||||||
|
|
из заданной вершины и вычислить их длины. |
||||||||||
1 |
|
3 |
|
|
|
|
|
|
|
|
|
|
|
4 |
1 |
2 |
4 |
5 |
|
3 |
6 |
|
7 |
8 |
|
|
|
|
|
|
|
|
|
|
|
|
||
5 |
|
6 |
n |
1 2 3 4 5 6 7 8 |
||||||||
|
|
|
π |
|
1 |
2 |
1 |
1 |
4 |
5 |
5 |
|
|
7 |
|
d |
0 |
1 |
2 |
1 |
1 |
2 |
2 |
2 |
|
8 Очередь:
2
4
7
8
Программа обхода графа с использованием контейнера.
public static void traverseWithContainer(Graph g, GraphVisitor visitor, int start, ContainerFactory factory) {
// Инициализация |
|
int n = g.vertices(); |
// Число вершин графа |
Container container = factory.createContainer(n); |
// Создание контейнера |
container.push(start); |
// Инициализация контейнера |
boolean[] inQueue = new boolean[n]; |
|
boolean[] passed = new boolean[n]; |
|
inQueue[start] = true; |
|
visitor.visitVertexIn(start); |
|
while (!container.isEmpty()) {
int current = container.pull(); passed[current] = true; visitor.visitVertexOut(current);
for (Iterator arcs = g.adjacentArcs(current); arcs.hasNext(); ) { Graph.Arc arc = (Graph.Arc)arcs.next();
int end = arc.getTo(); if (passed[end])
visitor.visitArcBackward(current, arc); else {
visitor.visitArcForward(current, arc, inQueue[end]); if (!inQueue[end]) {
container.push(end); inQueue[end] = true; visitor.visitVertexIn(end);
}
}
}
}
}
Программа поиска кратчайших путей в ненагруженном графе.
public static void findMinPaths(Graph g, |
// Исходный граф |
int start, |
// Стартовая вершина |
final int[] tree, |
// Результирующее дерево |
final int[] dist) |
// Массив расстояний |
{ |
|
dist[start] = 0; |
|
tree[start] = 0; |
|
traverseWithContainer(g, new GraphVisitor() {
// Коррекция пути производится при первом проходе по дуге вперед public void visitArcForward(int from, Graph.Arc arc, boolean retArc) {
if (!retArc) {
int end = arc.getTo(); dist[end] = dist[from] + 1; tree[end] = from;
}
}
},
start, new QueueFactory());
}
Алгоритмы поиска кратчайших путей в нагруженном графе.
Кратчайший путь между двумя вершинами – это путь с минимальным суммарным весом составляющих этот путь ребер
Кратчайшие пути из вершины (10):
|
|
2 |
|
1 |
1 |
|
|
3 |
|
4 |
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
||||||
|
2 |
|
4 |
5 |
|
|
V |
Длина |
Путь через |
|||||||
|
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|||||||
|
5 |
|
2 |
|
|
3 |
|
|
1 |
3 |
2 |
|||||
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
|
|
|
|
|
|
|
|
1 |
|
|
|
2 |
2 |
|
10 |
3 |
|
5 |
8 |
|
|
|
9 |
|
|||||||
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
3 |
4 |
|
||||||||
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
1 |
|
4 |
|
|
|
2 |
|
|
|
|
4 |
6 |
2, 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
4 |
2 |
|
|
6 |
|
4 |
7 |
|
|
|
|
|
|
6 |
4 |
3 |
||
|
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
8 |
2, 8 или 3, 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
6 |
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
7 |
2, 8 |
Кратчайший путь между двумя вершинами существует, если в графе нет цикла с суммарным отрицательным весом.
Алгоритм релаксации ребра при поиске кратчайших путей.
|
|
2 |
|
1 |
1 |
3 |
4 |
Пусть уже найдены оценки |
|
|
|
|
|
||||
|
|
|
2 |
4 |
|
5 |
3 |
кратчайших путей для вершин, |
|
2 |
|
|
соединенных ребром. |
||||
|
|
|
|
|
|
|
|
|
|
|
5 |
|
|
6 |
|
|
|
|
|
|
|
|
|
|
d[8] = 6; |
|
10 |
3 |
3 |
|
5 |
8 |
1 |
9 |
|
|
|
|
d[7] = 9 |
|||||
|
|
|
|
|
|
|
|
|
|
5 |
1 |
4 |
|
|
2 |
|
|
|
|
|
|
|
|
|||
|
|
6 |
|
4 |
7 |
|
|
|
|
|
|
|
89 |
|
|
Релаксация ребра (u, v):
if (d[u] + w(u,v) < d[v]) d[v] = d[u] + w(u,v);
Релаксация ребра (7, 8): |
Релаксация ребра (8, 7): |
9 + 2 > 6 |
6 + 2 < 9 d[7] = 8 |
Инициализация: |
|
d[start] = 0; d[i] = ∞ |
|
Последовательная релаксация ребер приведет к нахождению кратчайших путей. В каком порядке и сколько раз производить релаксацию ребер?
Алгоритм Дейкстры поиска кратчайших путей.
Запустить алгоритм обхода графа, в качестве контейнера выбрать очередь с приоритетами. Приоритет – текущая величина найденного расстояния от начальной вершины. Релаксации подвергаются прямые и обратные ребра.
|
|
2 |
|
2 |
1 |
|
3 |
|
4 |
|
|
|
|
|
|
|
|||
|
6 |
|
1 |
3 |
|
1 |
|
|
4 |
|
|
4 |
|
|
|
||||
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
10 |
1 |
3 |
|
5 |
8 |
|
|
3 |
9 |
|
|
|
|
|
|||||
|
3 |
1 |
1 |
|
|
1 |
|
|
|
4
6
7
n |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
π |
|
|
|
|
|
|
|
|
|
|
2 |
5 |
10 |
8 |
6 |
3 |
6 |
2 |
8 |
|
|
d |
|
|
|
|
|
|
|
|
|
|
6 |
4 |
1 |
8 |
3 |
2 |
6 |
7 |
10 |
0 |
|
|
|
|
|
|
|
|
|
|
|
|
Очередь
3
5
2
7
1
8
4
9
Программа для реализации алгоритма Дейкстры поиска кратчайших путей.
public static void DijkstraPath(Graph g, int start, final int[] tree, final double[] dist) {
for (int i = 0; i < g.vertices(); i++) { dist[i] = Double.MAX_VALUE; } dist[start] = 0;
traverseWithContainer(g, new DepthVisitor() {
public void visitArcForward(int from, Graph.Arc arc, boolean retArc)
{
if (dist[from] + arc.getWeight() < dist[arc.getTo()]) { dist[arc.getTo()] = dist[from] + arc.getWeight(); tree[arc.getTo()] = from;
}
}
}, start, new SimplePrioQueueFactory(dist));
}
Время работы алгоритма (max): время обхода графа (n + m) плюс время на организацию работы очереди с приоритетами (n log n)
Кратчайшие пути в ориентированном графе
1.Если в ориентированном графе нет дуг с отрицательным весом, то алгоритм Дейкстры работает точно так же, как и в случае неориентированных графов.
2.Если в ориентированном графе нет циклов с отрицательным весом, то можно применить алгоритм Беллмана – Форда.
2 |
1 |
|
2 3 -3
2
4 |
-2 |
5 |
|
1
3
7
2
2
2
9
-1 3
2
3
2
2
6
|
-1 |
4 |
2 |
|
8 
-1
n |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
|
|
|
|
|
|
|
|
|
|
π |
3 |
4 |
8 |
|
4 |
8 |
5 |
9 |
7 |
|
|
|
|
|
|
|
|
|
|
d |
1 |
2 |
2 |
0 |
-2 |
-1 |
-1 |
0 |
1 |
|
|
|
|
|
|
|
|
|
|
И так далее… В конце концов получится…
