Обход графов

advertisement
Лекция 7
Динамические структуры данных. Графы и их представление.
Основные операции.
Основные понятия теории графов
Граф (graph) – объект, состоящий из Вершина
множества кружков (точек) и множество
соединяющих их линий. Кружки (точки)
называются вершинами графа (nodes), линии со
стрелками – дугами (arcs), без стрелок – ребрами
Ребро
(edges). Т.е. граф – это пара G=(V,E), где V множество вершин, а E - семейство пар ребер
Дуга
ei=(vi1, vi2), vij принадлежит V. Вершины графа
можно использовать для представления объектов,
а дуги — для отношений между объектами.
Графы обычно изображаются в виде геометрических фигур. В
классическом графе отсутствуют петли, то есть ребра, соединяющие
вершину саму с собой.
Граф,
в
котором
направление
линий
2
принципиально
(линии
являются
дугами)
1
называется ориентированным (орграф). В отличие
от ребер, дуги соединяют две неравноправные
5
вершины: одна из них называется началом дуги
3
(дуга из нее исходит), вторая - концом дуги (дуга в
4
нее входит). Пример: G=(V,E): V={1, 2, 3, 4, 5};
E={(1,2), (1,5), (3,1), (5,2), (5,3), (5,4)}.
Граф, в котором направление линий не выделяется (все линии являются
ребрами), называется неориентированным (две вершины равноправны, нет
никакой разницы между "началом" и "концом" ребра).
Чаще всего рассматривают графы, в которых все ребра имеют один тип –
либо ориентированные, либо неориентированные.
Две вершины v и u называются смежными, если они соединены ребром
(дугой) е: e=(v,u). Смежные вершины называются граничными вершинами
соответствующего ребра (дуги), а это ребро (дуга) - инцидентным
соответствующим вершинам. Любому ребру инцидентно ровно две вершины,
а вершине может быть инцидентно произвольное количество ребер.
Два ребра называются смежными, если они инцидентны одной
вершине.
Степенью вершины в неориентированном графе называется число
инцидентных ей ребер. Для ориентированного графа различают исходящую
степень, определяемую как число выходящих из него
ребер, и входящую степень, определяемую как число
входящих в нее ребер. Сумма исходящей и входящей
vi
vj
степеней
называется
степенью
вершины.
Изолированная вершина – это вершина со степенью 0
(нуль-граф).
Мультигаф – из одной вершины в другую можно перейти разными
способами (граф с кратными ребрами).
Путем называется последовательность вершин v1, v2, …, vn, для которой
существуют ребра (или дуги в орграфе) v1  v2, v2  v3, ..., vn-1  vn. Этот
путь начинается в вершине v1 и, проходя через вершины v2,
v3, ..., vn-1, заканчивается в вершине vn. Длина пути—
количество дуг (ребер), составляющих путь, в данном случае
длина пути равна n – 1. Путь называется простым, если все
вершины на нем, за исключением, может быть, первой и
последней, различны. Для неориентированного графа на
рис.рисунка путями будут adbc и abc.
Замкнутый путь без повторяющихся ребер называется
циклом (или контуром в орграфе); без повторяющихся вершин (кроме
первой и последней) - простым циклом.
Полным называется граф, в котором проведены все возможные ребра.
Для графа, имеющего n вершин, таких ребер будет n(n-1)/2.
Вершина v достижима из вершины u, если существует путь,
начинающийся в u и заканчивающийся в v.
Граф называется связным, если все его вершины взаимно достижимы.
На рисунке изображен связный граф.
Если граф не является связным, то его можно разбить на связные
подграфы, называемые компонентами.
Компонента связности – это максимальный связный подграф. В общем
случае граф может состоять из произвольного количества компонент
связности. Любая изолированная вершина является отдельной компонентой
связности. Например, в приведённом ниже графе содержится 4 компоненты
связности: abhk, gd, c, f.
Взвешенный граф – это граф, некоторым элементам которого
(вершинам, ребрам или дугам) сопоставлены числа. Числа-пометки носят
названия вес, длина, стоимость.
Длина пути во взвешенном графе – это сумма весов ребер (дуг), из
которых состоит путь.
Способы представления графов в памяти
Графический способ представления графов
вычислительной машины. Поэтому существуют
представления графов.
непригоден для
другие способы
Матрица смежности – это матрица n×n, где n - число вершин. Строка
с номером i содержит 1 в строке с номером j, если
существует дуга из вершины i в вершину j.
Пример. Составить матрицу смежности для
взвешенного графа:
Решение:
a
b
c
d
a
0
1
1
0
0
b
1
0
2
1
0
с
1
2
0
3
0
d
0
1
3
0
0
Задание 1. Создать матрицу смежности для графа Невзвешенный граф
можно интерпретировать как взвешенный, все ребра которого имеют
одинаковый вес 1:
Решение:
Небольшое затруднение возникнет в том
a
b
c
d
a
0
1
1
0 случае, если в
b
1
0
1
0 графе разрешаются
с
1
1
0
1 ребра с весом 0.
придется
d
0
0
1
0 Тогда
хранить
два
массива: один с нулями и единицами,
которые служат показателем наличия ребер, а
второй - с весами этих ребер.
Задание 2. Неориентированный граф
задан матрицей смежности. Нарисовать граф.
Ответ:
1
2
3
4
5
6
0
1
1
0
0
0
1
1
0
0
0
1
1
0
2
0
1
0
0
0
0
3
0
0
0
0
1
0
4
3
0
0
1
0
0
0
5
0
0
0
0
0
0
6
2
4
5
6
Задание 3. Составить матрицу смежности для следующего графа:
Ответ:
1
2
3
4
5
1
0
1
1
0
0
2
1
0
0
1
1
3
1
0
0
1
0
4
0
1
1
0
0
5
0
1
0
0
0
Матрица смежности для неориентированного графа будет симметричной
относительно своей главной диагонали, а для орграфа - несимметричной.
Удобство матрицы смежности состоит в наглядности и прозрачности
алгоритмов, основанных на ее использовании. А неудобство - в несколько
завышенном требовании к памяти: если граф далек от полного, то в массиве,
хранящем матрицу смежности, оказывается много "пустых мест" (нулей).
Матрица инцидентности
Матрица инцидентности имеет размер m*n, где n – количество вершин,
m – количество дуг графа. Элемент матрицы, соответствующий k-дуге и i-ой
вершине, равен
 +1, если дуга выходит из вершины;
 –1, если дуга входит в вершину
 и нули во всех остальных строках.
Пример. Для неориентированного графа на рисунке
матрица инцидентности:
Задание 1. Составить
следующего графа:
матрицу
инцидентности
для
Ответ:
1
2
3
4
5
a
1
-1
0
0
0
b
0
1
-1
0
0
c
0
0
1
-1
0
d
-1
0
0
1
0
e
0
-1
0
1
0
Задание 2. Ориентированный граф задан матрицей инцидентности.
Вершины обозначены номерами 1, 2, 3, 4, 5, 6, а ребра латинскими буквами a,
b, c, d, e, f, g, h, i.
a
b
c
d
e
f
g
h
i
1 1 0 1 0 0 0 0 0 0 


2 1 1 0 1 0 1 0 0 0 
3 0 1 0 0 1 0 1 1 0 


4  0 0 0 0 0  1  1 0  1
5 0 0 1 1 1 0 0 0 0 


6  0 0 0 0 0 0 0  1 1 
Нарисовать граф.
Ответ:
a
1
2
b
d
c
6
3
f
e
h
4
5
i
Список ребер
Этот способ задания наиболее удобен для внешнего представления
входных данных. Каждая строка входного файла содержит информацию об
одном ребре или дуге в следующем виде:
<номер начальной вершины> <номер конечной вершины>
[<вес ребра>]
Например, для следующих графов получим списки:
a b
a
a c
b c
b
b d
c
c d
c f
d
f
f d
b f
a
1
10
2
b
10
c
3
a
a
b
b
c
b
c
c
d
d
1
10
2
10
3
d
Списки смежности
Этот способ задания графов подразумевает, что для каждой вершины
будет указан список всех смежных с нею вершин (для ориентированного
графа – список вершин, являющихся концами исходящих дуг):
<номер начальной вершины>: <номера смежных вершин>
Пример: (рис. графов см. выше)
1)
a: b c
b: c d f
c: d f
d: f
2)
a: b 1 c 10
b: a 1 c 2 d 10
c: a 10 d 3
d: b 10 c 3
Этот способ представления графов является внутренней реализацией
списка смежности: в одном линейном списке содержатся номера начальных
вершин, а в остальных – списки исходящих из них дуг. Введем следующие
типы данных для представления графов:
Type refnode=^node;
refarc=^arc;
node=record {список вершин}
id:integer; {номер вершины}
infnode:integer; {вес}
next:refnode;
{указатель на следующую вершину в
списке}
arclist:refarc {указатель на список дуг}
end;
arc=record {список дуг определенной вершины}
infarc:integer; {вес}
next:refarc; {указатель на следующую дугу, исходящую из
данной вершины}
adj1:refnode {указатель на вершину, в которую ведет данная
дуга}
end;
В
универсальном
динамическом
представлении
используется
линейный список. Для любой вершины можно хранить ее вес.
Например, для графа следующего вида
типы данных для представления графов:
1
от adjacency – смежность
Type refnode=^node;
refarc=^arc;
node=record {список вершин}
id:integer; {номер вершины}
infnode:integer; {вес}
next:refnode;
{указатель на следующую вершину в
списке}
arclist:refarc {указатель на список дуг}
end;
arc=record {список дуг определенной вершины}
infarc:integer; {вес}
next:refarc; {указатель на следующую дугу, исходящую из
данной вершины}
adj:refnode {указатель на вершину, в которую ведет данная
дуга}
end;
получим следующее представление:
nil
1
nil
2
nil
3
4
nil
nil
Задание. Нарисовать списковое представление графа (см. задание 2
раздела «Матрица инцидентности».
Рассмотрим основные процедуры работы с графами.
Основные процедуры работы с графами
Добавление вершины
Процедура AddNode добавляет в граф вершину с заданным номером n и
весом x. Предполагается, что уникальность номера обеспечивается
вызывающей программой.
Procedure AddNode(Var graph: refnode; n,x:integer);
Var p:refnode;
Begin new(p);
With p^ do
Begin id:=n;
Infnode:=x;
Arclist:=nil;
Next:graph
End;
graph:=p;
end;
Добавление дуги
Процедура добавления дуги AddArc в граф использует в качестве
входной информации ссылки на соединяемые вершины и значения вес
создаваемой дуги. Обе вершины должны существовать к моменту
выполнения процедуры.
Procedure AddArc(u,v:refnode; y:integer);
Var a:refarc;
Begin if u=nil or v=nil then writeln(‘Отсутствует вершина’)
Else begin
New(a);
With a^ do begin
Infarc:=y;
Adj:=v;
Next:=u^.arclist
End;
u^.arclist:=a
end
end;
Удаление дуг и вершин
Для удаления дуги указываются две ссылки на вершины, которые эта
дуга соединяет. Приведенная ниже процедура удаляет элемент из списка дуг,
выходящих из вершины u, если в нем записана ссылка на вершину v. Если
таких элементов несколько, то удаляется первый встретившийся.
Procedure DeleteArc(u,v:refnode);
Var a,before:refarc;
Run:boolean;
Begin
If u<>nil then Begin
a:=u^.arclist;
run:=truel
while a<>nil and run do
if a^.adj=v then run:=false
else begin
before:=a;
a:=a^.next
end;
if a<>nil then begin
if a=u^.arclist then u^.arclist:=a^.next
else before^.next:=a^.next;
dispose(a)
end
end
end;
Процедура удаления вершины более сложная, так как кроме удаления
дескриптора вершины и подцепленного к ней списка выходящих дуг,
необходимо просмотреть всю оставшуюся часть графа в поисках висячих
ссылок. Эти ссылки удаляются с помощью процедуры DeleteArc.
Procedure DeleteNode(Var graph:refnode; v:refnode);
Var before,p,q:refnode;
a,after:refarc;
begin
p:=graph;
while p<>nil do {цикл по всем вершинам графа}
begin
q:=p^.next;
if p<>v then begin
DeleteArc(p,v) {удаление дуг, направленных удаляемой
вершине}
before:=p;
end
else Begin {удаление вершины v и всех дуг, исходящих из
нее}
if p=graph then graph:=q; {если это первая вершина}
a:=p^.arclist;
{удаление списка дуг вершины v}
while a<>nil do
begin
after:=a^.next;
dispose(a);
a:=after
end;
if p=graph then graph:=q
else before^.next:=q;
dispose(p);
end;
p:=q;
end
end;
Просмотр графа
Просмотр графа заключается в посещении всех вершин и дуг графа и
выводе в стандартный файл всей информации о графе. Для непустого графа
информация группируется по вершинам (списки смежности).
Procedure BrowseGraph(graph:refnode);
Var a:refarc;
Nn,na: integer; {счетчики количества вершин и дуг}
Begin
Nn:=0; na:=0;
while graph<>nil do
begin
writeln(‘Вершина
’,
graph^.id,’
с
весом
’
,graph^.infnode);
writeln(‘Список дуг:’);
a:=graph^.arclist;
if a=nil then writeln(‘пуст’);
while a<>nil do begin
writeln(‘Дуга к вершине ’, (a^.adj)^.id,’, вес дуги
’,a^.infarc);
na:=na+1;
a:=a^.next;
end;
nn:=nn+1;
graph:=graph^.next;
end;
writeln(‘В графе ’, nn, ‘вершин и ‘, na,’ дуг’)
end;
Алгоритмы на графах: поиск в глубину и поиск в ширину,
алгоритм Дейкстры
Обход графов
Просмотр заключается в посещении всех вершин и дуг графа выводе
всей информации о графе.
Обход (поиск) в глубину
Метод поиска в глубину (depth first search, DFS) — обобщение метода
обхода дерева в прямом порядке.
Обход в глубину2 (называемый иногда стандартным обходом), есть
обход графа по следующим правилам:
1. Добавляет начальную вершину в стек и помечает её как
использованную.
2. Рассматривает верхнюю вершину стека и добавляет в стек
первую попавшуюся смежную ей неиспользованную вершину,
Поиск в глубину в чистом виде может быть использован, например, для построения дерева
подкаталогов. Встаём на корневой каталог, выбираем первый попавшийся подкаталог, далее выбираем в нём
какой-либо подкаталог и т.д. пока не доберёмся до пустого (в смысле подкаталогов) каталога. Тогда
откатываемся на родительский каталог и выбираем в нём следующий подкаталог, продолжая в нём
описанные действия. Как только в каком-либо каталоге просмотрены все подкаталоги, снова откатываемся
назад на родительский. К моменту, когда нам придётся откатываться из корневого каталога, мы побываем во
всех подкаталогах.
Стоит заметить что структура каталогов представляет собой дерево, а поиск глубину работает на
любом графе. Он строит дерево в процессе своей работы (дерево поиска в глубину).
Представим теперь, что рёбра графа есть коридоры лабиринта, а вершины - комнаты, соединённые
этими коридорами. Мы попадаем в начальную комнату, мы умеем помечать комнаты и нам нужно обойти
их все. Пометим нашу комнату и будем делать следующее: выбираем первый попавшийся коридор и ходим
по нему; если мы попадаем в помеченную комнату, возвращаемся и выбираем следующий по часовой
стрелке коридор (пройдя по всем коридорам комнаты, возвращаемся из неё по коридору, по которому в неё
пришли), а если нет, то помечаем новую комнату и продолжаем выбирать коридоры указанным способом.
Когда мы пройдем по всем коридорам из начальной комнаты, мы точно побываем в каждой комнате
лабиринта.
2
помечая её как использованную. Если такой вершины нет,
извлекает вершину стека.
3. Если стек не пуст, переходит к пункту 2.3
Таким образом, этот алгоритм обходит все вершины, достижимые из
начальной, и каждую вершину обрабатывает не более одного раза.
Текст процедуры обхода в глубину (рекурсивный вариант):
var a:array[1..nmax,1..nmax]of integer;// матрица смежности
used:array[1..nmax]of boolean; // список меток
n:integer; // количество вершин графа
procedure dfs(v:integer);
var
i:integer;
begin
used[v]:=true; // помещенная в стек вершина помечается
использованной
for i:=1 to n do // рассматриваются все ребра, исходящие
из этой вершины
// проверка, использована ли вершина, в которую ведет
выбранное ребро, если да, то поиск продолжается из этой вершины.
if (a[v,i]=1)and(not used[i]) then
dfs(i);
end;
...
dfs(X); // здесь X - номер начальной вершины
При выполнении обхода графа по этим правилам стремимся проникнуть
"вглубь" графа так далеко, как это возможно, затем отступаем на шаг назад и
снова стремимся пройти вперед и так далее.
Пример:
Для графа указать порядок включения
вершин в множество достижимых вершин,
если поиск начинается с вершины v1, при
обходе в первую очередь предпочтение
отдается вершине с меньшим номером.
3
или другое объяснение:
 находясь в вершине x, нужно двигаться в любую другую, ранее не посещенную вершину (если
таковая найдется), одновременно запоминая дугу, по которой впервые попали в данную вершину;
 если из вершины x не можем попасть в ранее не посещенную вершину или таковой вообще нет, то
возвращаемся в вершину z, из которой впервые попали в x, и продолжаем обход в глубину из
вершины z.
Ответ: v1, v2, v3, v4,v5, v6,
v8,v9, v7.
Задание:
Неориентированный граф задан матрицей
смежности. Нарисовать граф. Указать порядок
включения вершин в множество достижимых
вершин, если поиск в глубину начинается с
вершины 1, при обходе в первую очередь
предпочтение отдается вершине с меньшим
номером.
Ответ:
Путь, полученный методом поиска в глубину, в общем случае не
является кратчайшим путем из вершины v в вершину u.
Поиск количества компонент связности графа
Использование обхода в глубину для поиска компонент связности графа:
For I := 1 to N do // перебираем все вершины графа
If not Use[I] Then Begin // текущая вершина не помечена
dfs(I); // запуск поиска в глубину
Inc(K);
связности
End;
// увеличение числа найденных компонент
Обход (поиск) в ширину
Обход графа в ширину (breadth first search, BFS) основывается на
замене стека очередью:
1. Добавляет начальную вершину в очередь и помечает её как
использованную.
2. Извлекает следующую вершину из очереди и добавляет в
очередь смежные ей неиспользованные вершины, помечая их как
использованные.
3. Если очередь не пуста, переходит к пункту 2.
После такой модификации чем раньше посещается вершина (помещается
в очередь), тем раньше она используется (удаляется из очереди).
Использование вершины происходит с помощью просмотра сразу всех еще
не просмотренных вершин, смежных этой вершины. Таким образом, "поиск
ведется как бы во всех возможных направлениях одновременно".4
Очередность просмотра вершин при поиске в ширину:
4
Если мы представим, что рёбра есть трубы с одинаковой шириной, то подобное "расползание"
очень напоминает распространение воды по трубам если начать эту самую воду равномерно заливать в
начальную вершину.
Пример:
Порядок включения вершин при использовании метода поиска в
ширину: v1, v2, v3, v4, v7, v8, v6, v5,v9.
Писать поиск в ширину, как и большинство других алгоритмов, лучше
для графа, заданного списком рёбер. В этом случае алгоритм более мобилен
(это важно при модификациях) и даёт оптимальное время работы.
procedure bfs(v:integer);
var Og:array[1..nn]of integer;
yk1,yk2:integer;
i:integer;
begin
// в элементе og[i] хранится номер группы вершины i.
Изначально номер группы всех вершин кроме стартовой равен 0, это
значит, что они ещё не были использованы.
for i:=1 to n do
og[i]:=0;
// Инициализация очереди, т.е. добавление в неё начальной
вершины с номером v
yk2:=0;
yk1:=1;
Og[yk1]:=v;
used[v]:=true; // пометка вершины использованной
while yk2 < yk1 do // цикл работает, пока очередь не пуста
begin
inc(yk2);v:=Og[yk2];
write(v:2);
// просматриваются все рёбра, исходящие из первой вершины
очереди
for i:=1 to n do
// использована ли вершина, в которую ведёт выбранное ребро,
если нет , то вершина добавляется в очередь
if (a[v,i] <> 0) and not used[i] then
begin
// сдвигается указатель конца очереди
inc(yk1);
Og[yk1]:=i;
used[i]:=true;
end;
end;
end;
Чаще всего поиск в ширину используется для
нахождения кратчайшего пути от одной вершины X до
другой – Y: нужно запустить поиск в ширину из
вершины X и при добавлении новой вершины в
очередь смотреть, не является ли она вершиной Y.
Если
программа
a
2
1
не завершит
b
работу
в
условном
f
d
3
c
цикле,
это
6
значит, что
e
пути из X в Y не существует.
h
4
5
i
Задание 1
Для графа указать порядок включения вершин в множество достижимых
вершин, если поиск начинается с вершины 1, в первую очередь отдается
предпочтение вершине с меньшим номером, и используется
а) метод поиска в глубину
б) метод поиска в ширину.
Ответ:
Поиск в глубину: 1, 2, 3, 4, 6, 5.
Поиск в ширину: 1, 2, 5, 3, 4, 6.
Задание 2. Поиск начинается с вершины 2.
Ответ:
Поиск в глубину: 2, 3, 4, 6, 5.
Поиск в ширину: 2, 3, 4, 5, 6.
Задача нахождения кратчайшего пути. Алгоритм Дейкстры
Пусть есть ориентированный граф, у которого все дуги имеют
неотрицательные метки (стоимости дуг), а одна вершина определена как
источник. Задача состоит в нахождении стоимости кратчайших путей от
источника ко всем другим вершинам графа G (здесь длина пути определяется
как сумма стоимостей дуг, составляющих путь). Эта задача часто называется
задачей нахождения кратчайшего пути с одним источником.
Пример. Рассмотрим работу алгоритма Дейкстры на примере графа,
показанного на рисунке. Пусть требуется найти расстояния от 1-й вершины
до всех остальных. Кружками обозначены вершины, линиями — пути между
ними (ребра графа).
В кружках обозначены номера
Первый шаг. Минимальную
вершин, над ребрами обозначена их метку имеет вершина 1. Ее
«цена» — длина пути. Рядом с каждой соседями являются вершины 2, 3 и
вершиной красным обозначена метка 6.
— длина кратчайшего пути в эту
вершину из вершины 1
Первый
по
очереди
сосед
Аналогичную
операцию
вершины 1 — вершина 2, потому что проделываем с двумя другими
длина пути до нее минимальна. Длина соседями 1-й вершины — 3-й и 6-й.
пути в нее через вершину 1 равна
кратчайшему расстоянию до вершины
1 + длина ребра, идущего из 1 в 2, то
есть 0 + 7 = 7. Это меньше текущей
метки вершины 2, поэтому новая метка
2-й вершины равна 7.
Все
соседи
вершины
1
проверены. Текущее минимальное
расстояние до вершины 1 считается
окончательным и пересмотру не
подлежит
(то,
что
это
действительно так, впервые доказал
Дейкстра). Вычеркнем её из графа,
чтобы отметить, что эта вершина
посещена.
Второй шаг. Шаг алгоритма
Снова пытаемся уменьшить
повторяется.
Снова
находим метки соседей выбранной вершины,
«ближайшую»
из
непосещенных пытаясь пройти в них через 2-ю.
вершин. Это вершина 2 с меткой 7.
Соседями вершины 2 являются 1, 3,
4.
Первый (по порядку) сосед
вершины 2 — вершина 1. Но она
уже посещена, поэтому с 1-й
вершиной ничего не делаем.
Следующий сосед вершины 2 —
вершины 4 и 3. Если идти в неё через
2-ю, то длина такого пути будет =
кратчайшее расстояние до 2 +
расстояние между вершинами 2 и 4 = 7
+ 15 = 22. Поскольку 22<∞,
устанавливаем метку вершины 4
равной 22.
Ещё один сосед вершины 2 —
вершина 3. Если идти в неё через 2,
то длина такого пути будет = 7 + 10
= 17. Но текущая метка третьей
вершины равна 9<17, поэтому метка
не меняется.
Все
соседи
вершины
2
Третий шаг. Повторяем шаг
просмотрены,
замораживаем алгоритма, выбрав вершину 3.
расстояние до неё и помечаем её как После её «обработки» получим
посещенную.
такие результаты:
Повторяем шаг алгоритма для оставшихся вершин (Это будут по
порядку 6, 4 и 5).
Завершение выполнения алгоритма. Алгоритм заканчивает работу, когда
вычеркнуты все вершины. Результат его работы: кратчайший путь от
вершины 1 до 2-й составляет 7, до 3-й — 9, до 4-й — 20, до 5-й — 20, до 6-й
— 11.
Поскольку алгоритм Дейкстры всякий раз выбирает для обработки
вершины с наименьшей оценкой кратчайшего пути, можно сказать, что он
относится к «жадным» алгоритмам.
Дан орграф G с множеством вершин V(G). Массив w — это двумерный
массив стоимостей, где элемент w[i, j] равен стоимости дуги i  j. Если дуги
i  j не существует, то w[i, j] ложится равным , т.е. большим любой
фактической стоимости дуг. В графе выделены две вершины, s и f, начало и
конец пути.
В общем случае может существовать несколько различных путей из s в f.
С каждой вершиной v графа связана переменная l[v], называемая меткой. На
каждом шаге l[v] содержит длину текущего кратчайшего особого пути к
вершине v. В процессе работы метка изменяется (временная метка). Метка
становится постоянной, когда найден самый короткий путь от вершины s к
вершине v. Алгоритм заканчивает работу, когда постоянной становится
метка от s до f.
Переменная p принимает в качестве своих значений вершины графа. Н –
множество вершин, имеющих временные метки.
Алгоритм Дейкстры
procedure Short(s, f);
...
begin
for v  V(G) do l[v]:=  ; { инициализация l }
l[s]:=0;
p:=s; // рабочая переменная
H:=V(G)-s; // множество вершин, имеющих временные метки
While p <> f do begin
for v  Out(p)  H do l[v]:= (min[l[v],l[p]+w[p,v]);
m:=f; // m – минимальная метка
for v  H do if l[m]>l[v] then m:=v;
H:=H-m; p:=m;
end;
end;
Задание. Для ориентированного графа нарисовать матрицу смежности.
Используя алгоритм Дейкстры, найти минимальные путь между вершиной
№1 и всеми остальными. Выполнение алгоритма описать пошагово. В
результате должен быть представлен сам путь (пути), а не только
минимальные значения.
1 2 3 4 5
1 0

2 0
3 0

4 0
5  0
10
0
0
0
0
0
50
0
20
0
30
0
0
0
0
1
В начале l
100 

0 
10 

60 
0 
2
3
4
5
(метки
0
вершин):
p=1
Н={2, 3, 4, 5}
Шаг 1
l
(метки
вершин):
Н={3, 4, 5}
Шаг 2
l
(метки
вершин):
Н={ 3, 5}
Шаг 3
l
(метки
вершин):
Н={5}
Шаг 4
l
(метки
вершин):
Н={ }




10
{1, 2}
p=2

30
{1, 4}
100
{1, 5}
60
{1, 2, 3}
30
{1, 4}
p=4
100
{1, 5}
50
{1, 4, 3}
p=3
90
{1, 4, 5}
60
{1, 4, 3, 5}
p=5
Несложно внести изменения в алгоритм так, чтобы можно было
определить сам кратчайший путь (т.е. последовательность вершин) для
любой вершины. Для этого надо ввести еще один массив Р1 вершин, где
P1[v] содержит вершину, непосредственно предшествующую вершине v в
кратчайшем пути. В программе надо записать условный оператор с условием
l[p]+w[p,v]<l[v], при выполнении которого элементу P1[v] присваивается
значение p. После выполнения алгоритма кратчайший путь к каждой
вершине можно найти с помощью обратного прохождения по
предшествующим вершинам массива Р1.
Type graph=array [1..nn,1..nn] of integer;
procedure short(n:integer; // количество вершин
W:graph;
// матрица смежности
s,f:integer; // начальная и конечная вершины
var length: integer; // длина пути
var Weight:real; //
var Path: array[1..NN] of integer);
var
v,k,j,m:byte;
l:array[1..nn] of real; // метки
p1:array[1..nn] of integer;
p:integer;
// рабочая переменная для вершин
H:Set Of byte; // множество вершин, имеющих временные метки
begin
H:=[];
// инициализация l
For v:=1 to n do begin
l[v]:=MaxInt; H:=H+[v];p1[v]:=0;
end;
l[s]:=0; p:=s;
H:=H-[s];
// выбор из Н такой вершины, что значение l минимально
while p<>f do begin
for v:= 1 to n do
If v In H then
if w[p,v]<MaxInt then
if l[v]> l[p]+w[p,v] then begin
l[v]:= l[p]+w[p,v]; p1[v]:=p end;
end;
m:=f;
for v:=1 to n do
if v In H then begin
if l[m]>l[v] then m:=v end;
H:=H-[m]; p:=m;
end;
// поиск пути
Path[1]:=f; length:=2; j:=f;
While j<>s Do Begin
Path[length]:=P1[j]; j:=P1[j]; length:=length+1
end;
For j:=1 to length div 2 Do Begin
k:=Path[j];
Path[j]:=Path[length-j];
Path[lengthj]:=k;
end;
length:=length-1;
Weight:=l[f];
end;
1 2
Задание 1. Имеется 8 городов и матрица
стоимостей перевозок между городами.
Нарисовать полученный граф. Используя
алгоритм Дейкстры, необходимо найти пути
минимальной стоимости между городами №1
и №8. Выполнение алгоритма описать
пошагово. В результате должен быть
представлен сам путь (пути), а не только
значение минимальной стоимости.
3 4
5
6
7 8
1  3  7   8  


2  3  4 12 6  10  
3  4  6 1 2   


4  7 12 6  5 3 9 12 
5   6 1 5  11 6  


6    2 3 11  8  


7  8 10  9 6 8  14 
8     12   14  
Пример применения алгоритма Дейкстры
Необходимо найти все кратчайшие пути от вершины №1 для графа, представленого на
рисунке
Составим матрицу длин кратчайших дуг для данного графа
∞
10
18
8
∞
∞
10
∞
16
9
21
∞
∞
16
∞
∞
15
∞
7
9
∞
∞
∞
12
∞
∞
∞
∞
∞
23
∞
∞
15
∞
23
∞
Cтартовая вершина, от которой строится дерево кратчайших путей - вершина 1.
Задаем стартовые условия: d(1)=0, d(x)=∞
Окрашиваем вершину 1, y=1.
Находим ближайшую вершину к окрашенной нами, используя формулу: d(x)=min{d(x);
d(y)+ ay,x}
d(2)=min{d(2) ; d(1)+a(1,2)}=min{∞; 0+10}=10
d(3)=min{d(3) ; d(1)+a(1,3)}=min{∞; 0+18}=18
d(4)=min{d(4) ; d(1)+a(1,4)}=min{∞; 0+8}=8
d(5)=min{d(5) ; d(1)+a(1,5)}=min{∞; 0+∞}=∞
d(6)=min{d(6) ; d(1)+a(1,6)}=min{∞; 0+∞}=∞
Минимальную длину имеет путь от вершины 1 до вершины 4 d(4)=8. Включаем вершину
№4 в текущее ориентированноe дерево, а так же дугу ведущую в эту вершину. Согласно
выражению это дуга (1,4)
d(2)=min{d(2) ; d(4)+a(4,2)}=min{10; 8+9}=10
d(3)=min{d(3) ; d(4)+a(4,3)}=min{18; 8+∞}=18
d(5)=min{d(5) ; d(4)+a(4,5)}=min{∞; 8+∞}=∞
d(6)=min{d(6) ; d(4)+a(4,6)}=min{∞; 8+12}=20
Минимальную длину имеет путь от вершины 1 до вершины 2 d(2)=10. Включаем вершину
№2 в текущее ориентированноe дерево, а так же дугу ведущую в эту вершину. Согласно
выражению это дуга (1,2)
d(3)=min{d(3) ; d(2)+a(2,3)}=min{18; 10+16}=18
d(5)=min{d(5) ; d(2)+a(2,5)}=min{∞; 10+21}=31
d(6)=min{d(6) ; d(2)+a(2,6)}=min{20; 10+∞}=20
Минимальную длину имеет путь от вершины 1 до вершины 3 d(3)=18. Включаем вершину
№3 в текущее ориентированноe дерево, а так же дугу ведущую в эту вершину. Согласно
выражению это дуга (1,3)
d(5)=min{d(5) ; d(3)+a(3,5)}=min{31; 18+15}=31
d(6)=min{d(6) ; d(3)+a(3,6)}=min{20; 18+∞}=20
Минимальную длину имеет путь от вершины 1 до вершины 6 d(6)=20. Включаем вершину
№6 в текущее ориентированноe дерево, а так же дугу ведущую в эту вершину. Согласно
выражению это дуга (4,6)
d(5)=min{d(5) ; d(6)+a(6,5)}=min{31; 20+23}=31
Минимальную длину имеет путь от вершины 1 до вершины 5 d(5)=31. Включаем вершину
№5 в текущее ориентированноe дерево, а так же дугу ведущую в эту вершину. Согласно
выражению это дуга (2,5)
Мы получили ориентированное дерево кратчайших путей начинающихся в вершине №1
для исходного графа .
d(1)=1 Длина маршрута L=0
d(2)=1-2 Длина маршрута L=10
d(3)=1-3 Длина маршрута L=18
d(4)=1-4 Длина маршрута L=8
d(5)=1-2-5 Длина маршрута L=31
d(6)=1-4-6 Длина маршрута L=20
Ориентированное дерево с корнем в вершине №1:
Пример лабораторной работы
Вариант 1 (с разбором решения и пояснениями)
Задача 1
Выполнить топологическую сортировку для приведенного ниже графа, описав весь
ход рассуждений пошагово (процедура сортировки описана ниже):
Procedure ChangeNum;
Var head:pt;
(*Указатель на начало стека.*)
nm,v,i:Integer;
t:pt;
Begin
head:=Nil;
nm:=0;
(*Текущий новый номер вершины.*)
For i:=1 То N Do
If Deg[i]=0 Then
(*Первоначальное
While Free(head)
Begin
(*Пока стек не
рассмотрены ранее на
WriteStack(head,i);
занесение в стек.*)
Do
пуст. Процедуры WriteStack,
занятии по стеку.*)
ReadStack
и
функция
Free
ReadStack(head,v);
Inc(nm);
NewNum[v]:=nm;
(*Присваиваем новый номер вершине, выбранной из стека.*)
t:=A[v];
While t<>Nil Do
Begin
(*Просматриваем вершины, связанные с данной.*)
Dec(Deg[f.data]);
{*Уменьшаем степень этой вершины на единицу.*)
If Deg[t^.data]=0 Then WriteStack(head, t^.data);
(*Если оказалось, что степень вершины после этого равна нулю, то заносим
ее в стек.*)
t:=t^.next;
(*Переходим к следующей вершине, связанной дугой с данной.*)
End;
End;
End;
Решение
Рассмотрим трассировку процедуры для указанного графа.
Списки смежности:
A[1] -> 2
A[2] -> nil
A[3] -> 1 6
A[4] -> 1
A[5] -> 3 4
A[6] - > 2
Массив Deg (предполагается, что он формируется отдельной процедурой)
Deg[1]=2
Deg[2]=2
Deg[3]=1
Deg[4]=1
Deg[5]=0
Deg[6]=1
Nm=0
1 выполнение цикла
Состояние стека: 5
v=5
Состояние стека: nil
nm=1, NewNum[5]=1
t-> A[5]
Deg[3]=0
Deg[4]=0
Состояние стека: 4 3
2 выполнение цикла
Состояние стека: 4 3
v=4
Состояние стека: 3
nm=2, NewNum[4]=2
t-> A[4]
Deg[1]=1
3 выполнение цикла
Состояние стека: 3
v=3
Состояние стека: nil
nm=3, NewNum[4]=3
t-> A[3]
Deg[1]=0
Deg[6]=0
Состояние стека: 6 1
4 выполнение цикла
Состояние стека: 6 1
v=6
Состояние стека: 1
nm=4, NewNum[6]=4
t-> A[6]
Deg[2]=1
5 выполнение цикла
Состояние стека: 1
v=1
Состояние стека: nil
nm=5, NewNum[1]=5
t-> A[1]
Deg[2]=0
Состояние стека: 2
6 выполнение цикла
Состояние стека: 2
v=2
Состояние стека: nil
nm=6, NewNum[2]=6
t-> A[2]
Состояние стека: nil
Конец цикла.
Массив NewNum (новые номера вершин)
NewNum[1]=5
NewNum[2]=6
NewNum[3]=3
NewNum[4]=2
NewNum[5]=1
NewNum[6]=4
1
2
3
4
5
6
7
8
9
10
11
12
Задача 2
Пусть граф задан матрицей смежности:
1
2
3
4
5
6
7
8
0
1
0
0
0
0
0
0
1
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
1
0
0
0
1
1
0
0
0
0
0
0
1
0
1
0
0
0
1
1
0
1
0
0
0
1
1
0
1
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
1
0
0
0
0
9
0
0
0
0
0
1
0
1
0
0
0
0
Построить граф и выполнить обход в глубину.
10
0
0
0
0
0
0
0
0
0
0
0
1
11
0
0
0
0
0
0
0
0
0
0
0
1
12
1
0
0
1
0
1
0
0
0
1
1
0
Решение
В качестве вершин последовательно выступают вершины 2,4,6,5,8,9 (в скобках
указан также порядок посещения вершин при поиске в глубину). Вершина 9 не имеет
смежных с нею непросмотренных вершин. Поэтому из вершины 9 возвращаемся в
вершину 6, минуя уже использованные вершины 8,5. Очередной этап начинаем с вершины
6, переходя последовательно в вершины 7,3,13. На следующем этапе в качестве v
выступает вершина 1 и из нее осуществляется последовательный переход в вершины
12,10,11
Задача 3
Найти минимальное остовное дерево в неориентированном взвешенном графе,
изображенном на приведенном ниже рисунке:
Решение
Обозначим ребро, соединяющее вершины vi и vj через xij.
Для удобства использования приведенного выше алгоритма решения поставленной
задачи, составим матрицу длин ребер. В рассматриваемом графе количество вершин n=8,
следовательно, матрица длин ребер графа G будет иметь размерность 8×8 и выглядеть
следующим образом:
Согласно приведенному выше алгоритму, выбираем ребро минимальной длины
(c47=1) и вместе с инцидентными ему двумя вершинами относим к графу G2. Таким
образом,
.
Длина дерева G2 будет равна l(G2)=c47=1. Поскольку
, продолжаем работу
алгоритма. По четвертой и седьмой строкам ищем минимальное число (за исключением
использованной единицы) − ребро минимальной длины, инцидентное либо вершине v4,
либо v7. Выбираем ребро x46 с длиной c46=3 и вместе с вершиной v6 добавляем к графу
G2, обозначая его теперь как G3:
, при этом l(G3)=c47+c46=1+3=4.
Аналогично находим графы:
,
Поскольку i=8=n(G), работа алгоритма заканчивается.
Таким образом, искомое минимальное остовное дерево графа G − граф G8, длина
которого равна 22.
Задача 4
Имеется 6 городов и матрица стоимостей перевозок между городами. Нарисуйте
полученный граф. Используя алгоритм Дейкстры, необходимо найти пути минимальной
стоимости между городами 1 и всеми остальными горадами. Выполнение алгоритма
описать пошагово.
1
2
3
4
5
6
1
0
7
9
14
2
7
0
10
15
3
9
10
0
11
2
4
15
11
0
6
5
6
0
9
6
14
2
9
0
Решение
Примем первоначально значение «бесконечность» за кратчайший путь от вершины 1
до всех остальных вершин.
Определим метки для вершин, смежных с вершиной 1. Все соседи вершины 1
проверены. Текущее минимальное расстояние до вершины 1 считается окончательным и
пересмотру не подлежит. Вычеркнем её из графа, чтобы отметить, что эта вершина
посещена.
Находим «ближайшую» из непосещенных вершин. Это вершина 2 с меткой 7.
Пытаемся уменьшить метки соседей выбранной вершины, пытаясь пройти в них
через 2-ю. Соседями вершины 2 являются 1, 3, 4. Первый (по порядку) сосед вершины 2
— вершина 1. Но она уже посещена, поэтому с 1-й вершиной ничего не делаем.
Ещё один сосед вершины 2 — вершина 3. Если идти в неё через 2, то длина такого
пути будет = 7 + 10 = 17. Но текущая метка третьей вершины равна 9<17, поэтому метка
не меняется.
Следующий сосед вершины 2 — вершина 4. Если идти в неё через 2-ю, то длина
такого пути будет = кратчайшее расстояние до 2 + расстояние между вершинами 2 и 4 = 7
+ 15 = 22. Поскольку 22<∞, устанавливаем метку вершины 4 равной 22. Все соседи
вершины 2 просмотрены, замораживаем расстояние до неё и помечаем ее как
посещенную.
Следующая вершина для просмотра – вершина 3. Соседями этой вершины являются
2, 4 и 6. Вершина 2 уже помечена как просмотренная. Для вершины 4 получаем путь,
равный 9+11=20<22, поэтому помечаем ее новой меткой 20. Для вершины 6 получаем
путь, равный 9+2=1<14, поэтому также помечаем ее новой меткой.
Выполняем те же действия для вершины 5. У нее осталась одна непомеченная
соседняя вершина – 5. Для этой вершины получаем путь 11+9=20. Обозначаем ее новой
меткой.
Следующая вершина по порядку с кратчайшим весом – 4. У нее один
непросмотренный сосед – 5. Получаем путь 20+6>20, поэтому метка не меняется.
У вершины 5 непросмотренных соседей нет. Поэтому окончательно имеем
Метками обозначены кратчайшие пути до всех вершин от вершины 1.
Download