Вы находитесь на странице: 1из 13

Теория графов

Родоначальником теории графов принято считать математика Леонарда Эйлера (1707-


1783). Однако теория графов многократно переоткрывалась разными авторами при решении
различных прикладных задач.
Задача о Кенигсбергских мостах. На рис. 1 представлен схематический план
центральной части города Кенигсберг (ныне Калининград), включающий два берега реки
Перголя, два острова в ней и семь соединяющих мостов. Задача состоит в том, чтобы обойти
все четыре части суши, пройдя по каждому мосту один раз, и вернуться в исходную точку. Эта
задача была решена (показано, что решение не существует) Эйлером в 1736 году.

рис. 1
Задача о трех домах и трех колодцах. Имеется три дома и три колодца, каким-то
образом расположенные на плоскости. Провести от каждого дома к каждому колодцу
тропинку так, чтобы тропинки не пересекались (рис. 2). Эта задача была решена (показано,
что решение не существует) Куратовским в 1930 году.

рис. 2

Неформально, граф можно определить как набор вершин (города, перекрестки,


компьютеры, буквы, цифры кости домино, микросхемы, люди) и связей между ними: дороги
между городами; улицы между перекрестками; проводные линии связи между компьютерами;
слова, начинающиеся на одну букву и закачивающиеся на другую или эту же букву;
проводники, соединяющие микросхемы; родственные отношения, например, Алексей - сын
Петра.

1
ОСНОВНЫЕ СВОЙСТВА ГРАФОВ
Определение: Графом G(V,E) называется совокупность двух множеств – непустого
множества V (множества вершин) и множества E двухэлементных подмножеств множества V
(E – множество ребер).
Говорят, что ребро (a, b) соединяет вершины a и b. Если ребро e соединяет вершину a с
вершиной b и пара (a,b) считается упорядоченной, то это ребро называется ориентированным,
вершина a – его началом, вершина b – концом. Если же эта пара считается неупорядоченной,
то ребро называется неориентированным, а обе вершины – его концами.
Ориентированным называется граф, в котором E ⊆ V × V – множество упорядоченных
пар вершин вида (x,y), где x называется началом, а y – концом дуги. Дугу (x, y) записывают как
x→ y.

Если элементом множества E может быть пара одинаковых (не различных) элементов V,
то такой элемент множества E называется петлей, а граф называется графом с петлями (или
псевдографом).
Вершины, соединенные ребром, называются смежными. Ребра, имеющие общую
вершину, также называются смежными.
Ребро и любая из его двух вершин называются инцидентными.
Степенью вершины в неорентированном графе называется количество ребер,
соединяющих ее с другими вершинами. Вершина, степень которой равна 0, называется
изолированной. В ориентированном графе степень вершины равна сумме ее входящей и
исходящей степеней.
Маршрут в графе – это последовательность вершин x1, x2, …, xn, такая, что для каждого
i = 1, 2, …, n-1 вершины xi и xi+1 соединены ребром. Эти n-1 ребер называются ребрами
маршрута. Говорят, что маршрут проходит через них, а число n-1 называют длиной
маршрута. Говорят, что маршрут соединяет вершины x1 и xn, они называются соответственно
началом и концом маршрута, вершины x2, …, xn-1 называются промежуточными. Маршрут
называется замкнутым, если x1 = xn.
Путь – это маршрут, в котором все ребра различны. Путь называется простым, если и
все вершины в нем различны.
Цикл – это замкнутый путь. Цикл x1, x2, …, xn-1, x1 называется простым, если все
вершины x1, x2, …, xn-1 попарно различны. Граф без циклов называется ациклическим.
В графе на рис.3 последовательность вершин

2
• 2, 3, 5, 4 – не маршрут;
• 2, 3, 4, 5, 1, 4, 3 – маршрут, но не путь;
• 3, 1, 4, 5, 1, 2 – путь, но не простой;
• 2, 3, 1, 4, 5, 1, 2 – цикл, но не простой;
• 2, 3, 4, 5, 1, 2 – простой цикл.
1

2 3 4 5

Рис. 3. Обыкновенный граф.

Неориентированный граф называется связным, если в нем для любых двух вершин
имеется маршрут, соединяющий эти вершины. Ориентированный граф называется связанным,
если для любых двух вершин a и b имеется маршрут от a до b и маршрут от b до a.
Множество всех достижимых вершин называются связными компонентами графа.
Некоторые виды графов имеют специальные названия.
Полным называется неориентированный граф, в котором каждая пара вершин являются
смежными.
Ациклический неориентированный граф называется лесом, а связанный ациклический
неориентированный граф – деревом.

ПРЕДСТАВЛЕНИЕ ГРАФА
Для представления графа в памяти компьютера используются следующие способы:
1) в виде множества списков смежных вершин;
2) в виде матрицы смежности.
Списки смежности.
Представление графа в виде списков смежности использует массив из |V| списков, по
одному для каждой вершины из V. Для каждой вершины a перечисляются все смежные с ней
вершины, т.е. элементы множества V(a). Такой способ задания дает возможность быстрого
просмотра окрестности вершины. Например, на рис.4 представлен граф списками смежности,
в котором содержатся пары связанных между собой элементов (вершин): (1,2), (1,4), (2,3),
(2,5), (2,6), (3,6), (4,5)

3
1 2 3 1 2 4 /
2 1 3 5 6 /
3 2 6 /
4 5 6 4 1 5 /
5 2 4 /
6 2 3 /
Рис. 4. Список смежности для неориентированного графа.
Матрица смежности.
Представление графа с помощью матрицы смежности предполагает, что вершины
пронумерованы в некотором порядке числами 1, 2, …, |V|. В этом случае представление графа
G с помощью матрицы смежности представляет собой матрицу A=(aij) размером |V|x|V| такую,
что

 1, е с (лi, jи) ∈ E
Ai j = 
 0, е с (лi, jи) ∉ E
На рис.5 показан граф с занумерованными вершинами и его матрицы смежности.
1

2 3

4 5 6

Рис.5. Пример неориентированного графа.

Матрица смежности:
0 1 1 0 1 0
 
1 0 0 1 1 0
1 0 0 0 1 1
A = 
0 1 0 0 0 0
 
1 1 1 0 0 1
0 0 1 0 1 0
 

ОБХОД ГРАФОВ
Поиск в графе – это алгоритмический метод обхода графа, в основе которого лежит
систематический перебор вершин графа, такой что каждая вершина просматривается только
один раз.
1) Поиск в глубину (Depth First Search).
Введем следующие понятия:

4
Вершину, которая еще не посещена, будем называть новой. В результате посещения
вершина становится открытой и остается такой, пока не будут исследованы все инцидентные
ей ребра. После этого она превращается в закрытую.
Стратегия поиска в глубину состоит в том, чтобы идти вглубь графа, пока это
возможно. При выполнении поиска в глубину исследуются все ребра, выходящие из
последней открытой вершины, и мы покидаем вершину только тогда, когда не остается
неисследованных выходящих из нее ребер. При этом происходит возврат в вершину, из
которой была открыта текущая вершина. Этот процесс продолжается до тех пор, пока не
будут открыты все вершины, достижимые из исходной. Если при этом остаются неоткрытые
вершины, то одна из них выбирается в качестве новой исходной вершины, и поиск
возобновляется из нее. Процесс повторяется до тех пор, пока не будут открыты все вершины
графа.
При поиске в глубину в качестве активной выбирается та из открытых вершин, которая
была посещена последней. Для реализации такого правила выбора наиболее удобной
структурой хранения множества открытых вершин является стек: открываемые вершины
складываются в стек в том порядке, в каком они открываются, а в качестве активной
выбирается последняя вершина.
На рис.6 у вершин в скобках указана та очередность, в которой вершины графа
просматривались в процессе поиска в глубину.

Рис. 6. Пример обхода графа (поиск в глубину).


Рассмотрим процедуру реализующая обход графа в глубину:
Пусть элементы вектора Visited[1..n] определяет состояние вершины, т.е.
Visited[i]=true, если вершина просмотрена, false иначе.
Матрица A[n,n] определяет матрицу смежности заданного графа.
Program Depth_First;
var
A: array [1..20, 1..20] of 0..1;
visited: array [1..20] of boolean;

5
I,j,n:integer;
procedure dfs(v: integer); {v текущая вершина}
var i: integer;
begin
writeln(v); {вершина v не посещалась}
visited[v]:= true;
for i:= 1 to n do
if (a[v,i]=1) and (not visited[i]) then
dfs(i);
end;

procedure graph_dfs;
var i: integer;
begin
for i:= 1 to n do
visited[i]:=false;
for i:= 1 to n do
if not visited[i] then
dfs(i);
end;
begin
write('n= '); readln(n);
for i:=1 to n do
for j:=i+1 to n do
begin
write('Versina ',i,',',j,'= ');
readln(A[i,j]);
A[j,i]:=A[i,j]
end;
graph_dfs;
end.
Процедура помечает вершину V, затем проходит по всем вершинам и проверяет
каждую вершину, если она не помечена и смежная с данной вершиной V, то запускается от
нее.

6
2) Поиск в ширину (Breadth First Search)
Суть заключается в том, чтобы рассмотреть все вершины, связанные с текущей.
Принцип выбора следующей вершины для вершины a – выбирается та, которая была раньше
рассмотрена, т.е. находящиеся от нее на расстоянии 1, затем вершины, находящиеся от a на
расстоянии 2, и т.д.
Для реализации данного принципа необходима структура данных очередь, когда новая
вершина становится открытой, она добавляется в конец очереди, а активная выбирается в ее
начале.
Основная особенность поиска в ширину, отличающая его от других способов обхода
графов, состоит в том, что в качестве активной вершины выбирается та из открытых, которая
была посещена раньше других. Именно этим обеспечивается главное свойство поиска в
ширину: чем ближе вершина к старту, тем раньше она будет посещена.
На рис.7 рядом с вершинами в скобках указана очередность просмотра вершин графа.

Рис.7. Пример обхода графа (поиск в ширину).


Procedure DFS (m:Integer);
Var
Q:Array [1..20] Of integer; {Очередь}
head,tail:Integer;{Указатели очереди, head – номер текущей
вершины; tail – новые вершины, помещаемые в «хвост» очереди q}
visited: Array [1..20] Of Boolean; {отмечает уже пройденные
вершины}
i,v,k:Integer;
Begin
head:=1; tail:=1; {Начальная инициализация}
For i:=1 to n do
Visited[i]:=False;
q[tail]:=m;
visited[m]:=true; {B очередь помещаем вершину m}

while head<=tail do {Пока очередь не пуста}

7
begin
v:=q[head];
head:=head+1;
write(v); {Берем элемент из очереди}
For k:=1 to n do {Просмотр всех вершин, связанных с
вершиной v}
if (A[v,k]<>0) and (not Visited[k]) then
{Если вершина ранее не просмотрена, то заносим её номер в
очередь}
begin
tail:=tail+1;
q[tail]:=k;
Visited[k]:=True;
end;
end;
End;
КРАТЧАЙШИЕ ПУТИ
Дан ориентированный граф G = <V,E>, веса дуг – A[i,j] (i,j=l..N, где N – количество
вершин графа), начальная и конечная вершины – s, t ∈ V. Веса дуг записаны в матрице
смежности А, если вершины i и j не связаны дугой, то A[i,j] = 0. Путь между s и t оценивается

суммой ∑A[i,
i , j∈пути
j ] . Необходимо найти путь с минимальной оценкой. Оценку пути

назовем его весом или длиной.

Пример. Кратчайший путь в графе на рис.8. из вершины 1 в вершину 4 проходит через


3-ю и 2-ю вершины и имеет оценку 6.
2
5 2

1 1 4

3 6
3
Рис. 8. Ориентированный взвешенный граф.

Нам необходимо найти кратчайший путь, т. е. путь с минимальным весом, между


двумя вершинами графа. Эта задача разбивается на две подзадачи: сам путь и значение

8
минимального веса. Обозначим ее через D[s] как Array[1..N] Of Integer и на каждом шаге
определяем оценки от вершины s до всех остальных вершин графа.

Алгоритм Дейкстры
Дан ориентированный или неориентированный взвешенный граф с n вершинами и m
рёбрами. Веса всех рёбер неотрицательны. Указана некоторая стартовая вершина s. Требуется
найти длины кратчайших путей из вершины s во все остальные вершины и вывести сам
кратчайший путь.
Эта задача называется "задачей о кратчайших путях с единственным источником"
(single-source shortest paths problem).
Алгоритм
Опишем алгоритм, который предложил датский исследователь Дейкстра (Dijkstra) в
1959 г.
Определим массив d[], в котором для каждой вершины v будем хранить текущую длину
d[v] кратчайшего пути из s в v. Изначально записываем в d[s]=0 и в d[v] значения A[s,v], если
они достижимы из вершины s, а для всех остальных вершин эта длина равна бесконечности
(достаточно большое число, заведомо большее возможной длины пути): d[v] = ∞
Кроме того, для каждой вершины v будем хранить, помечена она ещё или нет, т.е.
определим массив visited[]. Изначально все вершины не помечены, т.е. visited [v]=true, если
вершина уже рассмотрена, и visited[v]=false, если нет. Изначально заполняем массив u
значениями false (вершины не обработаны) и visited [s]=true.
Определим массив p[], такой что p[v] – номер вершины, из которой нужно идти в
вершину v в текущем кратчайшем пути, т.е. предпоследняя вершина пути.
Изначально заполнить массив p значением s. Кратчайший путь можно будет
восстановить по нему, каждый раз беря предка от текущей вершины, пока мы не придём в
стартовую вершину s – так мы получим искомый кратчайший путь, но записанный в обратном
порядке.
P = (s, …, p[p[p[v]]], p[p[v]], p[v], v)
Алгоритм Дейкстры состоит из n итераций. На очередной итерации выбирается
вершина i с наименьшей величиной d[v] среди ещё не помеченных, т.е.:
d [v] = min d[ j]
j: visited [ j ]=0

На первой итерации выбрана будет стартовая вершина s.


Выбранная таким образом вершина v отмечается помеченной. Далее, на текущей
итерации, из вершины v производятся улучшения (релаксации): просматриваются все рёбра

9
(v,x), исходящие из вершины v, и для каждой такой вершины x алгоритм пытается улучшить
значение d[x], тогда:
d[x] = min (d[x], d[v] + A[v,x])
При каждой успешной релаксации, т.е. когда из выбранной вершины v происходит
улучшение расстояния до некоторой вершины x, мы записываем, что предком вершины x
является вершина v: p[x] = v
На этом текущая итерация заканчивается, алгоритм переходит к следующей итерации
(снова выбирается вершина с наименьшей величиной d, из неё производятся релаксации, и
т.д.). После n итераций, все вершины графа станут помеченными, и алгоритм свою работу
завершает. Утверждается, что найденные значения d[v] и есть искомые длины кратчайших
путей из s в v.

Инициализация:

1 2 3 4 5 6
Visite 1 0 0
0 0 0
d
D 0 7 9 ∞ ∞ 14
P 1 1 1 1 1 1

Шаг 1:
1 2 3 4 5 6
Visite
1 1 0 0 0 0
d
D 0 7 9 22 ∞ 14
P 1 1 1 2 1 1

10
Р

Шаг 2:
1 2 3 4 5 6
Visite
1 1 1 0 0 0
d
D 0 7 9 20 ∞ 11
P 1 1 1 3 1 3

Шаг 3:
1 2 3 4 5 6
Visite
1 1 1 0 0 1
d
D 0 7 9 20 20 11
P 1 1 1 3 6 3

Шаг 4 и Шаг 5 массивы не изменяются. В результате получим:


1 2 3 4 5 6
Visite
1 1 1 1 1 1
d
D 0 7 9 20 20 11
P 1 1 1 3 6 3

Маршрут из вершины 1 в вершину 5 будет:

Const
inf=65535;
type
matrix=array[1..50,1..50] of word;
var
D:array[1..100] of word; //массив кратчайших расстояний

11
P:array[1..100] of word; //массив вершин предков в кратчайшем
пути
visited:array[1..100] of boolean; //отмечаем, если посетили
A:Matrix;
s,i,j,n:integer;
procedure Deisktr (A : Matrix; N, s : integer);
var i, j, v, min, x, t, u: longint;
begin
for i:=1 to N do
begin
If A[s,i]<>0 then D[i]:=A[s,i] //изначальный массив расстояний
Else D[i]:=inf;
P[i]:=s;
visited[i]:=false;
end;
visited[s]:=TRUE; //вершина S посещена
d[s]:=0;
for i:=1 to n-1 do // на каждом шаге находим минимальное решение
и пытаемся его улучшить
begin
min:=inf;
for j:=1 to N do
if (not visited[j]) and (D[j] < min) then
begin
min:=D[j]; //минимальное расстояние
v:=j; //найденная вершина
end;
visited[v]:=TRUE; //и она отмечается посещенной
for x:= 1 to N do
if (D[x]>D[v]+A[v,x]) and (not visited[x]) and (A[v,x]<>0)
then //пытаемся улучшить решение. Если в ней расстояние
больше, чем сумма расстояния до текущей вершины и длины
ребра, то уменьшаем его.
begin
D[x]:=D[v] + A[v,x];
P[x]:=v; //запоминаем откуда пришли
end;
end;

12
write('finis='); readln(t);
u:=t; write(u);
while u<>s do begin
u:=p[u]; write(' ',u);
end;
end;
begin
write('n=');
Readln(n);
for i:=1 to n do
for j:=i+1 to n do
begin
write('Rebro ',i,',',j,'= ');
readln(A[i,j]);
A[j,i]:=A[i,j];
end;
write('start='); readln(s);
Deisktr(a,n,s);
end.

13