Алгоритм построения Эйлерова цикла — различия между версиями
(Отформатирован псевдокод, добавлена проверка на эйлеровость, добавлено описание удаления ребра за O(1), добавлены источники информации) |
|||
Строка 6: | Строка 6: | ||
<font size=2> | <font size=2> | ||
'''Код проверки графа на эйлеровость:''' | '''Код проверки графа на эйлеровость:''' | ||
− | function dfs(v : Vertex, vis[] : boolean): | + | '''boolean''' checkForEulerPath(): |
− | vis[v] = true | + | '''int''' numberOfOdd = 0 |
− | for (v, u) in E | + | '''for''' v '''in''' <tex>V</tex> |
− | if not vis[u] | + | '''if''' vertexDegree(v) '''mod''' 2 == 1 |
+ | numberOfOdd = numberOfOdd + 1 | ||
+ | '''if''' numberOfOdd > 2 <font color=darkgreen>// если количество вершин с нечетной степенью больше двух, то граф не является эйлеровым</font> | ||
+ | '''return''' ''false'' | ||
+ | '''boolean''' vis[sizeOf <tex>V</tex>] <font color=darkgreen>// инициализировать массив значениями ''false''</font> | ||
+ | '''for''' v '''in''' <tex>V</tex> | ||
+ | '''if''' vertexDegree(v) > 0 | ||
+ | dfs(v, vis) | ||
+ | break | ||
+ | '''for''' v '''in''' <tex>V</tex> | ||
+ | '''if''' vertexDegree(v) > 0 '''and''' '''not''' vis[v] <font color=darkgreen>// если количество компонент связности, содержащие ребра, больше одной,</font> | ||
+ | '''return''' ''false'' <font color=darkgreen> // то граф не является эйлеровым</font> | ||
+ | '''return''' ''true'' // граф является эйлеровым | ||
+ | |||
+ | <font color=darkgreen>// Вспомогательные функции:</font> | ||
+ | '''function''' dfs(v : Vertex, vis[] : '''boolean'''): | ||
+ | vis[v] = ''true'' | ||
+ | '''for''' (v, u) '''in''' <tex>E</tex> | ||
+ | '''if not''' vis[u] | ||
dfs(u, vis) | dfs(u, vis) | ||
− | int vertexDegree(v : Vertex): | + | '''int''' vertexDegree(v : Vertex): |
− | int count = 0 | + | '''int''' count = 0 |
− | for (v, u) in E | + | '''for''' (v, u) '''in''' <tex>E</tex> |
count = count + 1 | count = count + 1 | ||
− | return count | + | '''return''' count |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
'''Код построения эйлерова цикла:''' | '''Код построения эйлерова цикла:''' | ||
− | function findEulerPath(v : Vertex): | + | '''function''' findEulerPath(v : Vertex): |
Stack stack | Stack stack | ||
stack.push(v) | stack.push(v) | ||
− | while not stack.isEmpty() | + | '''while not''' stack.isEmpty() |
w = stack.top() | w = stack.top() | ||
− | if | + | '''if''' exists (w, u) '''in''' <tex>E</tex> |
stack.push(u) | stack.push(u) | ||
remove(w, u) | remove(w, u) | ||
− | else | + | '''else''' |
stack.pop() | stack.pop() | ||
print(w) | print(w) | ||
Строка 63: | Строка 64: | ||
== Рекурсивная реализация == | == Рекурсивная реализация == | ||
<font size=2> | <font size=2> | ||
− | findEulerPath(v): | + | '''function''' findEulerPath(v): |
− | for (v, u) in E | + | '''for''' (v, u) '''in''' <tex>E</tex> |
remove(v, u) | remove(v, u) | ||
findEulerPath(u) | findEulerPath(u) | ||
− | print v | + | print(v) |
</font> | </font> | ||
Версия 19:39, 10 января 2015
Содержание
Описание алгоритма
Алгоритм находит Эйлеров цикл как в ориентированном, так и в неориентированном графе. Перед запуском алгоритма необходимо проверить граф на эйлеровость. Чтобы построить Эйлеров путь, нужно запустить алгоритм из вершины с нечетной степенью.
Алгоритм напоминает поиск в глубину. Главное отличие состоит в том, что пройденными помечаются не вершины, а ребра графа. Начиная со стартовой вершины строим путь, добавляя на каждом шаге не пройденное еще ребро, смежное с текущей вершиной. Вершины пути накапливаются в стеке . Когда наступает такой момент, что для текущей вершины все инцидентные ей ребра уже пройдены, записываем вершины из в ответ, пока не встретим вершину, которой инцидентны не пройденные еще ребра. Далее продолжаем обход по не посещенным ребрам.
Псевдокод
Код проверки графа на эйлеровость:
boolean checkForEulerPath(): int numberOfOdd = 0 for v inif vertexDegree(v) mod 2 == 1 numberOfOdd = numberOfOdd + 1 if numberOfOdd > 2 // если количество вершин с нечетной степенью больше двух, то граф не является эйлеровым return false boolean vis[sizeOf ] // инициализировать массив значениями false for v in if vertexDegree(v) > 0 dfs(v, vis) break for v in if vertexDegree(v) > 0 and not vis[v] // если количество компонент связности, содержащие ребра, больше одной, return false // то граф не является эйлеровым return true // граф является эйлеровым // Вспомогательные функции: function dfs(v : Vertex, vis[] : boolean): vis[v] = true for (v, u) in if not vis[u] dfs(u, vis) int vertexDegree(v : Vertex): int count = 0 for (v, u) in count = count + 1 return count
Код построения эйлерова цикла:
function findEulerPath(v : Vertex):
Stack stack
stack.push(v)
while not stack.isEmpty()
w = stack.top()
if exists (w, u) in
stack.push(u)
remove(w, u)
else
stack.pop()
print(w)
Доказательство
Пусть
Покажем, что это маршрут содержащий все ребра.
Допустим, что в момент окончания работы алгоритма имеются еще не пройденные ребра. Поскольку граф связен, должно существовать хотя бы одно непройденное ребро, инцидентное посещенной вершине. Но тогда эта вершина не могла быть удалена из , и не мог стать пустым.
Будем говорить, что ребро представлено в или , если в какой-то момент работы алгоритма вершины и находятся рядом. Каждое ребро графа представлено в . Допустим в какой-то момент из в перемещена вершина , а следующей в лежит . Возможны 2 варианта:
- На следующем шаге перемещена в . Тогда представлено в .
- Сначала будет пройдена некоторая последовательность ребер, начинающаяся в вершине . Ввиду четности степеней эта последовательность может закончиться только в вершине , а значит она следующей попадет в и будет представлено в .
Отсюда понятно, что последовательность вершин в
Рекурсивная реализация
function findEulerPath(v):
for (v, u) in
remove(v, u)
findEulerPath(u)
print(v)
Время работы
Если реализовать удаление ребер за
(для этого для каждого ребра достаточно добавить свойство deleted типа boolean), то алгоритм будет работать за .