Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

Autor artykułu: mgr Jerzy Wałaszek

©2026 mgr Jerzy Wałaszek

Istnienie cyklu lub ścieżki Eulera

SPIS TREŚCI
Podrozdziały

Problem

Należy zbadać, czy dany graf posiada cykl lub ścieżkę Eulera.

Rozwiązanie dla grafu nieskierowanego

Ścieżka Eulera (ang Eulerian path) jest ścieżką w grafie, która przechodzi dokładnie jeden raz przez każdą jego krawędź. Aby taka ścieżka istniała, graf musi być spójny (z pominięciem wierzchołków o stopniu 0, czyli bez krawędzi) oraz każdy jego wierzchołek za wyjątkiem dwóch musi posiadać parzysty stopień.

Graf ze ścieżką Eulera:
obrazek
 
Graf bez ścieżki Eulera:
obrazek

Graf jest półeulerowski (ang. semi-Eulerian graph), jeśli zawiera ścieżkę Eulera.

Cykl Eulera (ang. Eulerian cycle) jest ścieżką w grafie, która przechodzi przez wszystkie jego krawędzie i kończy się w wierzchołku startowym. Aby istniał cykl Eulera, graf musi być spójny (z pominięciem wierzchołków o stopniu 0) oraz każdy jego wierzchołek musi posiadać stopień parzysty.

Graf z cyklem Eulera:
obrazek
 
Graf bez cyklu Eulera:
obrazek

Graf jest eulerowski (ang. Eulerian graph), jeśli zawiera cykl Eulera.

Algorytm jest następujący:

Przeglądamy kolejne wierzchołki grafu aż do momentu, gdy natrafimy na wierzchołek o niezerowej liczbie sąsiadów. To będzie nasz punkt startowy. Tworzymy licznik wierzchołków o nieparzystym stopniu i zerujemy go. Od tego wierzchołka uruchamiamy przejście DFS, które odwiedzi wszystkie wierzchołki, do których istnieją w grafie ścieżki z wierzchołka startowego. Zatem wyznaczy spójną składową zawierającą wierzchołek startowy. Przejście DFS pozwoli nam sprawdzić, czy graf jest spójny. W trakcie przechodzenia grafu DFS zlicza sąsiadów każdego odwiedzanego wierzchołka i, jeśli liczba ta jest nieparzysta, to zwiększa licznik wierzchołków o nieparzystym stopniu. Gdy przejście DFS się zakończy, kontynuujemy przeglądanie grafu od następnego wierzchołka po wierzchołku startowym. Jeśli natrafimy na nieodwiedzony wierzchołek, który posiada sąsiadów, to będzie to oznaczało, że graf nie jest spójny i nie może posiadać cyklu lub ścieżki Eulera. W takim przypadku kończymy algorytm z wynikiem negatywnym. Jeśli takiego wierzchołka nie spotkamy, to graf jest spójny (lub posiada dodatkowo jedynie wierzchołki bez krawędzi). W takim razie cykl Eulera będzie istniał w grafie, jeśli licznik wierzchołków o nieparzystym stopniu ma wartość 0. Jeśli licznik ten ma wartość 2, to będzie istniała ścieżka Eulera. W przeciwnym razie algorytm kończymy z wynikiem negatywnym.

Algorytm badający istnienie ścieżki lub cyklu Eulera w grafie nieskierowanym

Wejście:

n : liczba wierzchołków w grafie; n ∈ C.
graf : zadany w dowolnie wybrany sposób, algorytm tego nie precyzuje. Graf nie może posiadać pętli.

Wyjście:

Wynik:
0 – graf nie zawiera ścieżki lub cyklu Eulera.
1 – graf zawiera ścieżkę Eulera, lecz nie zawiera cyklu Eulera.
2 – graf zawiera cykl Eulera.

Elementy pomocnicze:

no : liczba wierzchołków o nieparzystym stopniu; no ∈ C.
i,v,u : wierzchołki grafu; i,v,u ∈ C.
nc : zlicza sąsiadów; nc ∈ C.
S : stos wierzchołków.
vs : n elementowa tablica logiczna odwiedzin.

Lista kroków:

K01: Znajdź pierwszy wierzchołek i o stopniu niezerowym
K02: Jeśli nie istnieje taki wierzchołek, ; graf nie ma krawędzi,
     to zakończ z wynikiem 1 ; kończymy
K03: Utwórz n elementową tablicę vs
K04: Wypełnij tablicę vs wartościami false
K05: Twórz pusty stos S
K06: no ← 0 ; zerujemy licznik wierzchołków
     ; o nieparzystym stopniu
K07: S.push(i) ; zapamiętujemy wierzchołek i-ty na stosie
K08: vs[i] ← true ; oznaczamy wierzchołek jako odwiedzony
K09: Dopóki S.empty() = false, ; uruchamiamy DFS, aby sprawdzić
     wykonuj kroki K10…K17 ; spójność oraz wyznaczyć stopnie wierzchołków
K10: vS.top(); S.pop() ; pobieramy wierzchołek ze szczytu stosu
K11: nc ← 0 ; zerujemy licznik sąsiadów
K12: Dla każdego sąsiada u wierzchołka v: ; przeglądamy sąsiadów
     wykonuj kroki K13…K16
K13:   ncnc+1 ; zwiększamy licznik sąsiadów
K14:   Jeśli vs[u] = true, ; szukamy nieodwiedzonego sąsiada
       to następny obieg pętli K12
K15:   vs[u] ← true ; zaznaczamy go jako odwiedzonego
K16:   S.push(u) ; i umieszczamy na stosie
K17: Jeśli nc jest nieparzyste, ; zwiększamy licznik wierzchołków
     to nono+1 ; o stopniu nieparzystym
K18: Dla v = i+1, i+2, …, n-1: ; szukamy nieodwiedzonego
     wykonuj krok K19 ; jeszcze wierzchołka
K19:   Jeśli (vs[v] = false) obrazek (v ma sąsiada), ; graf jest niespójny
       to zakończ z wynikiem 0
K20: Jeśli no = 0, ; liczba wierzchołków o nieparzystym
     to zakończ z wynikiem 2 ; stopniu równa zero
K21: Jeśli no = 2, ; dwa wierzchołki o nieparzystym stopniu
     to zakończ z wynikiem 1
K22: Zakończ z wynikiem 0 ; brak cyklu lub ścieżki Eulera

Zastanów się, jak zmodyfikować powyższy algorytm, aby uwzględniał również pętle (pętlę należy liczyć za dwie krawędzie).


Przykładowe programy

Uwaga:

Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich.

Program odczytuje definicję grafu nieskierowanego i sprawdza istnienie w nim ścieżki lub cyklu Eulera. Wynik jest wypisywany w oknie konsoli:
NOT AN EULERIAN GRAPH
SEMI-EULERIAN GRAPH
EULERIAN GRAPH
Dane przykładowe (przekopiuj do schowka i wklej do okna konsoli):
Graf z cyklem
Eulera
symbol
12 14
1 2
1 4
2 4
2 5
2 7
4 5
4 6
4 7
4 9
5 7
5 11
6 7
7 9
7 11
Graf ze ścieżką
Eulera
symbol
12 16
0 1
1 4
2 3
2 4
2 5
2 7
4 5
4 6
4 7
4 9
5 10
5 11
6 9
7 9
7 11
9 10
Graf bez ścieżki
i cyklu Eulera
symbol
12 17
0 1
1 4
2 3
2 4
2 5
2 7
4 5
4 6
4 7
4 9
5 6
5 10
5 11
6 9
7 9
7 11
9 10
Pascal
// Badanie, czy graf zawiera
// cykl lub ścieżkę Eulera
// Data: 4.01.2014
// (C)2013 mgr Jerzy Wałaszek
//---------------------------

program eulercp;

// Typy dla dynamicznej tablicy
// list sąsiedztwa oraz stosu
type
  PSLel = ^SLel;
  SLel =  record
    next  : PSLel;
    v     : integer;
  end;

TList = array of PSLel;

// Definicja typu obiektowego Stack
//---------------------------------
Stack = object
  private
    // lista przechowująca stos
    S : PSLel;
  public
    constructor init;
    destructor destroy;
    function   empty : boolean;
    function   top : integer;
    procedure  push(v : integer);
    procedure  pop;
end;

// Konstruktor
//------------
constructor Stack.init;
begin
  S := nil;
end;

// Destruktor
//-----------
destructor Stack.destroy;
begin
  while S <> nil do pop;
end;

// Sprawdza, czy stos jest pusty
//------------------------------
function Stack.empty : boolean;
begin
  if S = nil then
    empty := true
  else
    empty := false;
end;

// Zwraca liczbę ze szczytu stosu
//-------------------------------
function Stack.top : integer;
begin
  if S <> nil then
    top := S^.v
  else
    top := -MAXINT;
end;

// Umieszcza dane na stosie
//-------------------------
procedure Stack.push(v : integer);
var
  e : PSLel;
begin
  new(e);
  e^.v := v;
  e^.next := S;
  S := e;
end;

// Usuwa dane ze stosu
//--------------------
procedure Stack.pop;
var
  e :PSLel;
begin
  if S <> nil then
  begin
    e := S;
    S := S^.next;
    dispose(e);
  end;
end;

// Funkcja bada graf pod kątem
// posiadania cyklu lub
// ścieżki Eulera
// n - liczba wierzchołków w grafie
// graf - tablica list sąsiedztwa
// Wynik:
// 0 - graf nie zawiera ścieżki
//     lub cyklu Eulera
// 1 - graf zawiera ścieżkę Eulera
// 2 - graf zawiera cykl Eulera
//---------------------------------
function isEulerian(n : integer;
             var graf : TList)
                      : integer;
var
  no, nc, i, v, u : integer;
  S : Stack;
  vs : array of boolean;
  p : PSLel;

begin
  // Szukamy pierwszego
  // wierzchołka z sąsiadami
  i := 0;
  while (i < n) and
        (graf[i] = nil) do
    inc(i);
  // Graf nie ma krawędzi
  if i = n then Exit(1);
  // Tworzymy dynamicznie
  // tablicę odwiedzin
  SetLength(vs, n);
  // Wypełniamy ją wartościami false
  for v := 0 to n-1 do
    vs[v] := false;
  // Tworzymy stos
  S.init;
  // Zerujemy licznik wierzchołków
  // o stopniu nieparzystym
  no := 0;
  // Wierzchołek startowy na stos
  S.push(i);
  // oznaczamy go jako odwiedzony
  vs[i] := true;
  // Uruchamiamy przejście DFS,
  // aby wyznaczyć spójną składową
  // zawierającą wierzchołek startowy
  // oraz policzyć stopnie wierzchołków
  // i wyznaczyć liczbę wierzchołków
  // o stopniach nieparzystych
  while S.empty = false do
  begin
    // Pobieramy do v wierzchołek
    // ze stosu
    v := S.top;
    // Pobrany wierzchołek usuwamy
    // ze stosu
    S.pop;
    // Licznik sąsiadów na zero
    nc := 0;
    // Przeglądamy sąsiadów
    // wierzchołka v
    p := graf[v];
    while p <> nil do
    begin
      // Zwiększamy licznik sąsiadów
      inc(nc);
      u := p^.v;
      // Szukamy nieodwiedzonych
      // sąsiadów
      if not vs[u] then
      begin
        // Oznaczamy ich
        // jako odwiedzonych
        vs[u] := true;
        // i umieszczamy na stosie
        S.push(u);
      end;
      // Przechodzimy do następnego
      // sąsiada na liście
      p := p^.next;
    end;
    // Nieparzysta liczba sąsiadów?
    if nc mod 2 = 1 then inc(no);
  end;
  // Przeglądamy pozostałe
  // wierzchołki grafu
  for v := i+1 to n-1 do
    if (vs[v] = false) and
       (graf[v] <> nil) then
    begin
      // Usuwamy tablicę odwiedzin
      SetLength(vs, 0);
      // graf nie jest spójny
      Exit(0);
    end;
  // Usuwamy tablicę odwiedzin
  SetLength(vs, 0);
  // Graf zawiera cykl Eulera
  if no = 0 then Exit(2);
   // Graf zawiera ścieżkę Eulera
  if no = 2 then Exit(1);
  // Graf nie posiada ścieżki
  // lub cyklu Eulera
  Exit(0);
end;

// **********************
// *** Program główny ***
// **********************

var
  n, m, i, v1, v2 : integer;
  p, r : PSLel;
  A : TList;

begin
  // Czytamy liczbę wierzchołków
  // i krawędzi
  read(n, m);
  // Tworzymy tablicę
  // list sąsiedztwa
  SetLength(A, n);
  // Tablicę wypełniamy
  // pustymi listami
  for i := 0 to n-1 do
    A[i] := nil;
  // Odczytujemy kolejne
  // definicje krawędzi
  for i := 0 to m-1 do
  begin
    // Wierzchołek startowy
    // i końcowy krawędzi
    read(v1, v2);
    // Tworzymy nowy element
    new(p);
    // Numerujemy go jako v2
    p^.v := v2;
    // Dodajemy go na początek
    // listy A[v1]
    p^.next := A[v1];
    A[v1] := p;
    // Krawędź w drugą stronę,
    // bo graf jest nieskierowany
    new(p);
    p^.v := v1;
    p^.next := A[v2];
    A[v2] := p;
  end;
  writeln;
  case isEulerian(n, A) of
    0: writeln('NOT AN EULERIAN GRAPH');
    1: writeln('SEMI-EULERIAN GRAPH');
    2: writeln('EULERIAN GRAPH');
  end;
  // Usuwamy tablicę
  // list sąsiedztwa
  for i := 0 to n-1 do
  begin
    p := A[i];
    while p <> nil do
    begin
      r := p;
      p := p^.next;
      dispose(r);
    end;
  end;
  SetLength(A, 0);
end.
C++
// Badanie, czy graf zawiera
// cykl lub ścieżkę Eulera
// Data: 4.01.2014
// (C)2013 mgr Jerzy Wałaszek
//---------------------------

#include <iostream>

using namespace std;

// Typy dla dynamicznej tablicy
// list sąsiedztwa i stosu
struct SLel
{
  SLel * next;
  int v;
};

const int MAXINT = 2147483647;

class Stack
{
  private:
    // lista przechowująca stos
    SLel * S;

  public:
    Stack(); // konstruktor
    ~Stack(); // destruktor
    bool empty(void);
    int  top(void);
    void push(int v);
    void pop(void);
};

//---------------------
// Metody obiektu Stack
//---------------------

// Konstruktor
//------------
Stack::Stack()
{
  S = nullptr;
}

// Destruktor
//-----------
Stack::~Stack()
{
  while(S) pop();
}

// Sprawdza, czy stos jest pusty
//------------------------------
bool Stack::empty(void)
{
  return !S;
}

// Zwraca szczyt stosu
//--------------------
int Stack::top(void)
{
  if(S) return S->v;
  return -MAXINT;
}

// Zapisuje na stos
//-----------------
void Stack::push(int v)
{
  SLel * e = new SLel;
  e->v = v;
  e->next = S;
  S = e;
}

// Usuwa ze stosu
//---------------
void Stack::pop(void)
{
  if(S)
  {
    SLel * e = S;
    S = S->next;
    delete e;
  }
}

// Funkcja bada graf pod kątem
// posiadania cyklu lub ścieżki
// Eulera
// n - liczba wierzchołków w grafie
// graf - tablica list sąsiedztwa
// Wynik:
// 0 - graf nie zawiera ścieżki
//     lub cyklu Eulera
// 1 - graf zawiera ścieżkę Eulera
// 2 - graf zawiera cykl Eulera
//----------------------------------
int isEulerian(int n,
               SLel ** graf)
{
  int no, nc, i, v, u;
  Stack S;
  bool * vs;
  SLel *p;

  // Szukamy pierwszego wierzchołka
  // z sąsiadami
  for(i = 0; i < n && !graf[i]; i++);
  // Graf nie ma krawędzi
  if(i == n) return 1;
  // Tworzymy dynamicznie
  // tablicę odwiedzin
  vs = new bool [n];
  // Wypełniamy ją wartościami false
  for(v = 0; v < n; v++)
    vs[v] = false;
  // Zerujemy licznik wierzchołków
  // o stopniu nieparzystym
  no = 0;
  // Wierzchołek startowy na stos
  S.push(i);
  // oznaczamy go jako odwiedzony
  vs[i] = true;
  // Uruchamiamy przejście DFS,
  // aby wyznaczyć spójną składową
  // zawierającą wierzchołek startowy
  // oraz policzyć stopnie wierzchołków
  // i wyznaczyć liczbę wierzchołków
  // o stopniach nieparzystych

  while(!S.empty())
  {
    // Pobieramy do v wierzchołek
    // ze stosu
    v = S.top();
    // Pobrany wierzchołek usuwamy
    // ze stosu
    S.pop();
    // Licznik sąsiadów na zero
    nc = 0;
    // Przeglądamy sąsiadów
    // wierzchołka v
    for(p = graf[v]; p; p = p->next)
    {
      // Zwiększamy licznik sąsiadów
      nc++;
      u = p->v;
      // Szukamy nieodwiedzonych sąsiadów
      if(!vs[u])
      {
        // Oznaczamy ich
        // jako odwiedzonych
        vs[u] = true;
        // i umieszczamy na stosie
        S.push(u);
      }
    }
    // Nieparzysta liczba sąsiadów?
    if(nc % 2 == 1) no++;
  }
  // Przeglądamy pozostałe
  // wierzchołki grafu
  for(v = i = 1; v < n; v++)
    if(!vs[v] && graf[v])
    {
      // Usuwamy tablicę odwiedzin
      delete [] vs;
      // graf nie jest spójny
      return 0;
    }
  // Usuwamy tablicę odwiedzin
  delete [] vs;
  // Graf zawiera cykl Eulera
  if(!no) return 2;
  // Graf zawiera ścieżkę Eulera
  if(no == 2) return 1;
  // Graf nie posiada ścieżki
  // lub cyklu Eulera
  return 0;
}

// **********************
// *** PROGRAM GŁÓWNY ***
// **********************

int main()
{
  int n, m, i, v1, v2;
  SLel * p, * r, ** A;

  // Czytamy liczbę
  // wierzchołków i krawędzi
  cin >> n >> m;
  // Tworzymy tablicę
  // list sąsiedztwa
  A = new SLel * [n];
  // Tablicę wypełniamy
  // pustymi listami
  for(i = 0; i < n; i++)
    A[i] = nullptr;
  // Odczytujemy kolejne
  //definicje krawędzi
  for(i = 0; i < m; i++)
  {
    // Wierzchołek startowy
    // i końcowy krawędzi
    cin >> v1 >> v2;
    // Tworzymy nowy element
    p = new SLel;
    // Numerujemy go jako v2
    p->v = v2;
    // Dodajemy go na początek
    // listy A[v1]
    p->next = A[v1];
    A[v1] = p;
    // Krawędź w drugą stronę,
    // bo graf jest nieskierowany
    p = new SLel;
    p->v = v1;
    p->next = A[v2];
    A[v2] = p;
  }
  cout << endl;
  switch(isEulerian(n, A))
  {
    case 0: cout << "NOT AN EULERIAN GRAPH\n";
            break;
    case 1: cout << "SEMI-EULERIAN GRAPH\n";
            break;
    case 2: cout << "EULERIAN GRAPH\n";
            break;
  }
  // Usuwamy tablicę
  // list sąsiedztwa
  for(i = 0; i < n; i++)
  {
    p = A[i];
    while(p)
    {
      r = p;
      p = p->next;
      delete r;
    }
  }
  delete [] A;
  return 0;
}
Basic
' Badanie, czy graf zawiera
' cykl lub ścieżkę Eulera
' Data: 4.01.2014
' (C)2013 mgr Jerzy Wałaszek
'---------------------------

' Typy dla dynamicznej
' tablicy list sąsiedztwa i stosu

Type SLel
  next As SLel Ptr
  v As Integer
End Type

Type Stack
  Private:
    ' lista zawierająca stos
    S As SLel Ptr
  
  Public:
    Declare Constructor
    Declare Destructor
    Declare Function empty As Integer
    Declare Function top As Integer
    Declare Sub push(ByVal v As Integer)
    Declare Sub pop
End Type

Const MAXINT = 2147483647

'---------------------
' Metody obiektu Stack
'---------------------

' Konstruktor
'------------
Constructor Stack
  S = 0
End Constructor

' Destruktor
'-----------
Destructor Stack
  While S <> 0
    pop
  Wend
End Destructor

' Sprawdza,
' czy stos jest pusty
'--------------------
Function Stack.empty As Integer
  If S = 0 Then Return 1
  Return 0
End Function

' Zwraca szczyt stosu
'--------------------
Function Stack.top As Integer
  If S = 0 Then
    top = -MAXINT
  Else
    top = S->v
  End If
End Function

' Zapisuje na stos
'-----------------
Sub Stack.push(ByVal v As Integer)
  Dim e As SLel Ptr
  e = New SLel
  e->v = v
  e->next = S
  S = e
End Sub

' Usuwa ze stosu
'---------------
Sub Stack.pop
  Dim e As SLel Ptr
  If S Then
  	 e = S
  	 S = S->Next
  	 Delete e
  End If
End Sub

' Funkcja bada graf pod kątem posiadania
' cyklu lub ścieżki Eulera
' n - liczba wierzchołków w grafie
' graf - tablica list sąsiedztwa
' Wynik:
' 0 - graf nie zawiera ścieżki
'     lub cyklu Eulera
' 1 - graf zawiera ścieżkę Eulera
' 2 - graf zawiera cykl Eulera
'--------------------------------------
Function isEulerian(ByVal n As integer, _
 ByVal graf As SLel Ptr Ptr) _
            As Integer
  Dim As Integer no, nc, i, v, u
  Dim As Stack S
  Dim As Byte Ptr vs
  Dim As SLel Ptr p

  i = 0
  while (i < n) AndAlso _
        (graf[i] = 0)
    ' Szukamy pierwszego
    ' wierzchołka z sąsiadami
    i += 1
  Wend
  ' Graf nie ma krawędzi
  If i = n Then Return 1
  ' Tworzymy dynamicznie
  ' tablicę odwiedzin
  vs = New Byte [n]
  ' Wypełniamy ją wartościami false
  For v = 0 To n-1
    vs[v] = 0
  Next
  ' Zerujemy licznik wierzchołków
  ' o stopniu nieparzystym
  no = 0
  ' Wierzchołek startowy na stos
  S.push(i)
  ' oznaczamy go jako odwiedzony
  vs[i] = 1
  ' Uruchamiamy przejście DFS,
  ' aby wyznaczyć spójną składową
  ' zawierającą wierzchołek startowy
  ' oraz policzyć stopnie wierzchołków
  ' i wyznaczyć liczbę wierzchołków
  ' o stopniach nieparzystych
  While S.empty = 0
    ' Pobieramy do v wierzchołek
    ' ze stosu
    v = S.top
    ' Pobrany wierzchołek
    ' usuwamy ze stosu
    S.pop
    ' Licznik sąsiadów na zero
    nc = 0
    ' Przeglądamy sąsiadów wierzchołka v
    p = graf[v]
    While p <>0
      ' Zwiększamy licznik sąsiadów
      nc += 1
      u = p->v
      ' Szukamy nieodwiedzonych sąsiadów
      If vs[u] = 0 Then
        ' Oznaczamy ich
        ' jako odwiedzonych
        vs[u] = 1
        ' i umieszczamy na stosie
        S.push(u)
      End If
      ' Następny sąsiad na liście
      p = p->next
    Wend
    ' Nieparzysta liczba sąsiadów?
    If nc Mod 2 = 1 Then no += 1
  Wend
  ' Przeglądamy pozostałe
  ' wierzchołki grafu
  For v = i+1 To n-1
    If (vs[v] = 0) AndAlso _
       (graf[v] <> 0) Then
      ' Usuwamy tablicę odwiedzin
      Delete [] vs
      ' graf nie jest spójny
      Return 0
    End If
  Next
  ' Usuwamy tablicę odwiedzin
  Delete [] vs
  ' Graf zawiera cykl Eulera
  If no = 0 Then Return 2
  ' Graf zawiera ścieżkę Eulera
  If no = 2 Then Return 1
  ' Graf nie posiada
  ' ścieżki lub cyklu Eulera
  Return 0
End Function

' **********************
' *** PROGRAM GŁÓWNY ***
' **********************

Dim As Integer n, m, i, v1, v2
Dim As SLel Ptr p, r
Dim As SLel Ptr Ptr A

Open Cons For Input As #1
' Czytamy liczbę wierzchołków i krawędzi
Input #1, n, m
' Tworzymy tablicę list sąsiedztwa
A = new SLel Ptr [n]
' Tablicę wypełniamy pustymi listami
For i = 0 To n-1
  A[i] = 0
Next
' Odczytujemy kolejne definicje krawędzi
For i = 0 To m -1
  ' Wierzchołek startowy
  ' i końcowy krawędzi
  Input #1, v1, v2
  ' Tworzymy nowy element
  p = new SLel
  ' Numerujemy go jako v2
  p->v = v2
  ' Dodajemy go na początek listy A[v1]
  p->next = A[v1]
  A[v1] = p
  ' Krawędź w drugą stronę,
  ' bo graf jest nieskierowany
  p = new SLel
  p->v = v1
  p->next = A[v2]
  A[v2] = p
Next
Close #1
Print
Select Case isEulerian(n, A)
  case 0
    Print "NOT AN EULERIAN GRAPH"
  Case 1
    Print "SEMI-EULERIAN GRAPH"
  Case 2
    Print "EULERIAN GRAPH"
End Select
' Usuwamy tablicę list sąsiedztwa
For i = 0 To n-1
  p = A[i]
  While p
    r = p
    p = p->Next
    Delete r
  Wend
Next
Delete [] A
End
Python (dodatek)
# Badanie, czy graf zawiera
# cykl lub ścieżkę Eulera
# Data: 30.01.2025
# (C)2025 mgr Jerzy Wałaszek
#---------------------------

# Klasa dla dynamicznej
# tablicy list sąsiedztwa i stosu
class SLel:
    def __init__(self):
        self.next = None
        self.v = 0


MAXINT = 2147483647


class Stack:
    # Konstruktor
    def __init__(self):
        self.s = None


    # Destruktor
    def __del__(self):
        while self.s:
            self.pop()


    # Sprawdza,
    # czy stos jest pusty
    def empty(self):
       return not self.s


    # Zwraca szczyt stosu
    def top(self):
        global MAXINT
        if not self.s:
            return -MAXINT
        else:
            return self.s.v


    # Zapisuje na stos
    def push(self, v):
        e = SLel()
        e.v = v
        e.next = self.s
        self.s = e


    # Usuwa ze stosu
    def pop(self):
        if self.s:
            self.s = self.s.next


# Funkcja bada graf pod kątem
# posiadania cyklu lub ścieżki Eulera
# n - liczba wierzchołków w grafie
# graf - tablica list sąsiedztwa
# Wynik:
# 0 - graf nie zawiera ścieżki
#     lub cyklu Eulera
# 1 - graf zawiera ścieżkę Eulera
# 2 - graf zawiera cykl Eulera
def is_eulerian(n, graf):
    s = Stack()
    i = 0
    while (i < n) and not graf[i]:
        # Szukamy pierwszego
        # wierzchołka z sąsiadami
        i += 1
    # Graf nie ma krawędzi
    if i == n: return 1
    # Tworzymy dynamicznie
    # tablicę odwiedzin
    vs = [False] * n
    # Zerujemy licznik wierzchołków
    # o stopniu nieparzystym
    no = 0
    # Wierzchołek startowy na stos
    s.push(i)
    # oznaczamy go jako odwiedzony
    vs[i] = True
    # Uruchamiamy przejście DFS,
    # aby wyznaczyć spójną składową
    # zawierającą wierzchołek startowy
    # oraz policzyć stopnie
    # wierzchołków i wyznaczyć liczbę
    # wierzchołków o stopniach
    # nieparzystych
    while not s.empty():
        # Pobieramy do v wierzchołek
        # ze stosu
        v = s.top()
        # Pobrany wierzchołek
        # usuwamy ze stosu
        s.pop()
        # Licznik sąsiadów na zero
        nc = 0
        # Przeglądamy sąsiadów
        # wierzchołka v
        p = graf[v]
        while p:
            # Zwiększamy licznik
            # sąsiadów
            nc += 1
            u = p.v
            # Szukamy nieodwiedzonych
            # sąsiadów
            if not vs[u]:
                # Oznaczamy ich
                # jako odwiedzonych
                vs[u] = True
                # i umieszczamy
                # na stosie
                s.push(u)
            # Następny sąsiad
            # na liście
            p = p.next
        # Nieparzysta
        # liczba sąsiadów?
        if nc % 2: no += 1
    # Przeglądamy pozostałe
    # wierzchołki grafu
    for v in range(i+1,n):
        if not vs[v] and graf[v]:
            # Usuwamy
            # tablicę odwiedzin
            del vs
            # graf nie jest spójny
            return 0
    # Usuwamy tablicę odwiedzin
    del vs
    # Graf zawiera cykl Eulera
    if not no: return 2
    # Graf zawiera ścieżkę Eulera
    if no == 2: return 1
    # Graf nie posiada
    # ścieżki lub cyklu Eulera
    return 0


# **********************
# *** PROGRAM GŁÓWNY ***
# **********************

# Czytamy liczbę
# wierzchołków i krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy tablicę list sąsiedztwa
a = [None] * n
# Odczytujemy kolejne
# definicje krawędzi
for i in range(m):
    # Wierzchołek startowy
    # i końcowy krawędzi
    x = input().split()
    v1 = int(x[0])
    v2 = int(x[1])
    # Tworzymy nowy element
    p = SLel()
    # Numerujemy go jako v2
    p.v = v2
    # Dodajemy go na początek
    # listy a[v1]
    p.next = a[v1]
    a[v1] = p
    # Krawędź w drugą stronę,
    # bo graf jest nieskierowany
    p = SLel()
    p.v = v1
    p.next = a[v2]
    a[v2] = p
print()
match is_eulerian(n, a):
    case 0:
        print("NOT AN EULERIAN GRAPH")
    case 1:
        print("SEMI-EULERIAN GRAPH")
    case 2:
        print("EULERIAN GRAPH")
# Usuwamy tablicę list sąsiedztwa
for i in range(n):
    while a[i]:
        a[i] = a[i].next
del a
Wynik:
12 14
1 2
1 4
2 4
2 5
2 7
4 5
4 6
4 7
4 9
5 7
5 11
6 7
7 9
7 11

EULERIAN GRAPH
  12 16
0 1
1 4
2 3
2 4
2 5
2 7
4 5
4 6
4 7
4 9
5 10
5 11
6 9
7 9
7 11
9 10

SEMI-EULERIAN GRAPH
  12 17
0 1
1 4
2 3
2 4
2 5
2 7
4 5
4 6
4 7
4 9
5 6
5 10
5 11
6 9
7 9
7 11
9 10

NOT AN EULERIAN GRAPH

do podrozdziału  do strony 

Rozwiązanie dla grafu skierowanego

W grafie skierowanym istnieje cykl Eulera jeśli:

Ścieżka Eulera istnieje wtedy, gdy:

Z podanych powyżej informacji wynika, iż kluczowym warunkiem istnienia w grafie skierowanym cyklu lub ścieżki Eulera jest to, aby graf był silnie spójny poza wierzchołkami, które nie są incydentne z żadną krawędzią. Do rozwiązania tego problemu wykorzystamy nieco zmodyfikowany algorytm Tarjana wyznaczający silnie spójne składowe grafu. W trakcie przechodzenia przez graf procedura DFS będzie zbierała informacje o stopniach wejściowych i wyjściowych każdego wierzchołka oraz wyznaczała silnie spójne składowe, zapisując w tablicy C ich numery dla każdego z wierzchołków grafu. Następnie wyszukamy pierwszy wierzchołek o niezerowym stopniu. Wartość elementu tablicy C dla tego wierzchołka określi numer silnie spójnej składowej. Tworzymy liczniki: cinout, coutin, które będą zliczać wierzchołki o stopniach wchodzącym i wychodzącym różniących się o 1. Liczniki te zerujemy. Przeglądniemy wierzchołki wg poniższych zasad:

  1. Stopienie wchodzący i wychodzący równe zero – kontynuujemy z następnym wierzchołkiem.
  2. Wierzchołek musi należeć do silnie spójnej składowej. Jeśli nie, to graf nie jest silnie spójny, co daje wynik negatywny.
  3. Stopień wchodzący równy wychodzącemu – kontynuujemy z następnym wierzchołkiem.
  4. Jeśli stopień wchodzący-stopień wychodzący = 1, to zwiększ licznik cinout o 1. Jeśli cinout > 1, to zakończ z wynikiem negatywnym.
  5. Jeśli stopień wychodzący-stopień wchodzący = 1, to zwiększ licznik coutin o 1. Jeśli coutin > 1, to zakończ z wynikiem negatywnym.
  6. Jeśli stopnie różnią się więcej niż o 1, zakończ z wynikiem negatywnym

Gdy algorytm zakończy bez wyniku negatywnego przeglądanie wierzchołków grafu, to sprawdzamy stan liczników:

cinout = 1 i coutin = 1 – jest ścieżka Eulera (wystarczy sprawdzić tylko jeden z nich)

Inaczej mamy cykl Eulera

Algorytm badający istnienie ścieżki lub cyklu Eulera w grafie skierowanym

Algorytm przejścia DFSscc(v, cvn, VN, VLow, VS, Vind, Voutd, C, S, graf)

Wszystkie parametry za wyjątkiem v mogą być zrealizowane jako globalne, co zmniejszy zapotrzebowanie na pamięć przy wywoływaniach rekurencyjnych.

Wejście:

v : wierzchołek startowy; v ∈ C.
cvn : numer bieżącego wierzchołka; cvn ∈ C.
VN : tablica numerów wierzchołków, które ustala DFSscc( ).
VLow : tablica parametrów Low.
VS : tablica logiczna, która informuje, czy dany wierzchołek jest na stosie S.
Vind : tablica stopni wchodzących wierzchołków.
Voutd : tablica stopni wychodzących wierzchołków.
C : tablica numerów składowych, do których należą wierzchołki.
S : stos wierzchołków.
graf : graf zadany w dowolny sposób, algorytm tego nie precyzuje.

Wyjście:

Ustawione tablice: C, Vind Vout.

Elementy pomocnicze:

u : wierzchołek; u ∈ C.

Lista kroków:

K01: cvncvn+1 ; zwiększamy numer wierzchołka
K02: VN[v] ← cvn ; i zapamiętujemy go w tablicy VN
K03: VLow[v] ← cvn ; oraz wstępnie w tablicy VLow
K04: S.push(v) ; wierzchołek umieszczamy na stosie
K05: VS[v] ← true ; i zapamiętujemy w VS, że jest on na stosie
K06: Dla każdego sąsiada u wierzchołka v: ; przeglądamy kolejnych
     wykonuj kroki K07…K14 ; sąsiadów wierzchołka v
K07    Voutd[v] ← Voutd[v]+1 ; obliczamy stopień
       ; wychodzący wierzchołka v
K08    Vind[u] ← Vind[u]+1 ; i stopień wchodzący wierzchołka u
K09:   Jeśli VN[u] = 0, ; sprawdzamy, czy wierzchołek był odwiedzony
       to idź do kroku K13
K10:   Jeśli VS[u] = false, ; sprawdzamy, czy wierzchołek u jest na stosie
       to następny obieg pętli K06
K11:   VLow[v] ← min(VLow[v], VN[u]) ; jeśli tak,
       ; to wyznaczamy najmniejszy numer dostępnego wierzchołka z v
K12:   Następny obieg pętli K06 ; i kontynuujemy pętlę
K13:   DFSscc(u,cvn,VN,VLow,VS,S,Lscc,graf) ; wierzchołek nieodwiedzony:
       ; rekurencyjnie wywołujemy dla niego DFSscc()
K14:   VLow[v] ← min(VLow[v], VLow[u]) ; i wyznaczamy najmniejszy numer
       ; dostępnego wierzchołka z v
K15: Jeśli VLow[v] ≠ VN[v] ; sprawdzamy, czy znaleźliśmy
     to zakończ ; całą silnie spójną składową
K16: uS.top() ; pobieramy ze stosu wierzchołek silnie spójnej składowej
K17: S.pop() ; pobrany wierzchołek usuwamy ze stosu
K18: VS[u] ← false ; zaznaczamy, że wierzchołka już nie ma na stosie
K19: C[u] ← v ; w tablicy C zaliczamy wierzchołek u do składowej v
K20: Jeśli uv, ; pętlę kontynuujemy, aż do dotarcia do korzenia składowej
     to idź do kroku K16
K21: Zakończ

Algorytm główny: isEulerian(n,graf)

Wejście:

n : liczba wierzchołków w grafie; n ∈ C.
graf : graf zadany w dowolny sposób, algorytm tego nie precyzuje.

Wyjście:

0 – graf nie posiada cyklu i ścieżki Eulera.
1 – graf posiada ścieżkę Eulera.
2 – graf posiada cykl Eulera.

Elementy pomocnicze:

v : numer wierzchołka; v ∈ C.
cvn : numer bieżącego wierzchołka lub spójnej składowej; cvn ∈ C.
VN : tablica numerów wierzchołków, które ustala DFSscc( ).
VLow : tablica parametrów Low.
VS : tablica logiczna, która informuje, czy dany wierzchołek jest na stosie S.
S : stos wierzchołków.
Vind : tablica stopni wchodzących.
Voutd : tablica stopni wychodzących.
C : tablica numerów składowych, do których należą wierzchołki.
cinout : licznik wierzchołków o większych stopniach wchodzących; cinout ∈ C.
coutin : licznik wierzchołków o większych stopniach wychodzących; coutin ∈ C.

Lista kroków:

K01: Utwórz n elementowe tablice:
     VN, VLow, VS, Vind, Voutd i C
K02: Wyzeruj tablice:
     VN, VS, Vind i Vout
K03: Utwórz pusty stos S
K04: cvn ← 0 ; zerujemy numer startowy wierzchołków dla DFS
K05: Dla v = 0, 1, …, n-1:
     wykonuj:
       Jeśli VN[v] = 0, 
       to DFSscc(v,cvn,VN,VLow,VS,Vind,Voutd,C,S,graf)
       ; w nieodwiedzonym wierzchołku uruchamiamy DFS
K06: v ← 0
K07: Dopóki (v < n) obrazek (Vind[v]+Vout[v] = 0), ; szukamy
     wykonuj vv+1 ; wierzchołka o niezerowym stopniu
K08: Jeśli v = n, ; graf jest pusty
     to zakończ z wynikiem 0
K09: cvnC[v] ; zapamiętujemy numer składowej,
     ; do której należy v
K10: cinout ← 0 ; zerujemy liczniki
K11: coutin ← 0
K12: Dopóki v < n, ; przechodzimy przez wierzchołki grafu
     wykonuj kroki K13…K23
K13:   Jeśli Vind[v]+Voutd[v] = 0, ; pomijamy wierzchołki
       to idź do kroku K23 ; o stopniu zerowym
K14:   Jeśli C[v] ≠ cvn, ; graf nie jest silnie spójny
       to zakończ z wynikiem 0
K15:   Jeśli Vind[v] = Voutd[v], ; pomijamy wierzchołki
       to idź do kroku K23 ; o stopniach równych
K16:   Jeśli Vind[v]-Voutd[v] ≠ 1, ; różnica stopnia
       to idź do kroku K21 ; wchodzącego i wychodzącego = 1?
K17:   cinoutcinout+1 ; jeśli tak,
       ; zwiększamy odpowiedni licznik
K18:   Jeśli cinout > 1, ; licznik nie może być większy od 1
       to zakończ z wynikiem 0
K19:   Idź do kroku K23
K20:   Jeśli Voutd[v]-Vind[v] ≠ 1, ; stopnie zbytnio się różnią
       to zakończ z wynikiem 0
K21:   coutincoutin+1 ; zwiększamy odpowiedni licznik
K22:   Jeśli coutin > 1, ; który również nie może być większy od 1
       to zakończ z wynikiem 0
K23:   vv+1 ; następny wierzchołek
K24: Jeśli cinout = 1, ; jest ścieżka Eulera
     to zakończ z wynikiem 1
K25: Zakończ z wynikiem 2 ; lub cykl Eulera

Przykładowe programy

Uwaga:

Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich.

Program odczytuje definicję grafu nieskierowanego i sprawdza istnienie w nim ścieżki lub cyklu Eulera. Wynik jest wypisywany w oknie konsoli:
NOT AN EULERIAN GRAPH
SEMI-EULERIAN GRAPH
EULERIAN GRAPH
Dane przykładowe (przekopiuj do schowka i wklej do okna konsoli):
Graf z cyklem
Eulera
:
symbol 
9 17
0 1
1 3
1 4
2 1
2 5
3 0
3 2
3 7
4 2
4 3
4 6
5 4
5 7
6 3
7 4
7 8
8 5
Graf ze ścieżką
Eulera:

obrazek
9 17
0 1
0 7
1 2
1 5
2 3
2 4
3 0
3 7
4 5
4 8
5 2
5 7
6 3
7 4
7 6
7 8
8 1
Graf bez ścieżki
i cyklu Eulera:

obrazek
9 10
0 4
1 5
1 6
2 7
3 1
4 2
5 8
6 3
7 0
8 1
Pascal
// Badanie istnienia
// cyklu lub ścieżki Eulera
// Data: 3.02.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------

program scc;

// Typy dla dynamicznej tablicy
// list sąsiedztwa oraz stosu
type
  PSLel = ^SLel;
  SLel =  record
    next : PSLel;
    v    : integer;
  end;

TList = array of PSLel;

// Definicja typu obiektowego Stack
//---------------------------------
Stack = object
  private
    // lista przechowująca stos
    S : PSLel;

  public
    constructor init;
    destructor destroy;
    function   empty : boolean;
    function   top : integer;
    procedure  push(v : integer);
    procedure  pop;
end;

// Zmienne globalne
//-----------------
var
  n,m,cvn : integer;
  VN,VLow,Vind,Voutd,C : array of integer;
  VS : array of boolean;
  S : Stack;
  graf : array of PSLel;

//---------------------
// Metody obiektu Stack
//---------------------

// Konstruktor
//------------
constructor Stack.init;
begin
  S := nil;
end;

// Destruktor
//-----------
destructor Stack.destroy;
begin
  while S <> nil do pop;
end;

// Sprawdza, czy stos jest pusty
//------------------------------
function Stack.empty : boolean;
begin
  if S = nil then
    empty := true
  else
    empty := false;
end;

// Zwraca liczbę ze szczytu stosu
//----------------------------------
function Stack.top : integer;
begin
  if S = nil then
    top := -MAXINT
  else
    top := S^.v;
end;

// Umieszcza dane na stosie
//-------------------------
procedure Stack.push(v : integer);
var
  e : PSLel;

begin
  new(e);
  e^.v := v;
  e^.next := S;
  S := e;
end;

// Usuwa dane ze stosu
//--------------------
procedure Stack.pop;
var
  e : PSLel;
begin
  if S <> nil then
  begin
    e := S;
    S := S^.next;
    dispose(e);
  end;
end;

// Zwraca mniejszą z dwóch liczb
//------------------------------
function min(a, b : integer)
                  : integer;
begin
  if a < b then
    min := a
  else
    min := b;
end;

// Procedura wykonująca przejście DFS
// i wyznaczająca silnie spójną
// składową oraz stopnie wierzchołków
// v - wierzchołek startowy dla DFS
//-----------------------------------
procedure DFSscc(v : integer);
var
  u : integer;
  p : PSLel;
begin
  // Zwiększamy numer wierzchołka
  inc(cvn);
  // Numerujemy bieżący wierzchołek
  VN[v] := cvn;
  // Ustalamy wstępnie parametr Low
  VLow[v] := cvn;
  // Wierzchołek na stos
  S.push(v);
  // Zapamiętujemy,
  // że v jest na stosie
  VS[v] := true;
  // Przeglądamy listę sąsiadów
  p := graf[v];
  while p <> nil do
  begin
    // Wyznaczamy stopień wychodzący
    inc(Voutd[v]);
    // i wchodzący
    inc(Vind[p^.v]);
    // Jeśli sąsiad jest nieodwiedzony, to
    if VN[p^.v] = 0 then
    begin
      // wywołujemy dla niego
      // rekurencyjnie DFS
      DFSscc(p^.v);
      // i obliczamy parametr Low dla v
      VLow[v] := min(VLow[v],VLow[p^.v]);
    end
    // jeśli sąsiad odwiedzony,
    // lecz wciąż na stosie,
    else if VS[p^.v] then
      // to wyznaczamy parametr Low dla v
    VLow[v] := min(VLow[v],VN[p^.v]);
    p := p^.next;
  end;
  // Sprawdzamy,
  //  czy mamy kompletną składową
  if VLow[v] = VN[v] then
  begin
    repeat
      // Pobieramy wierzchołek ze stosu
      u := S.top;
      // Pobrany wierzchołek
      // usuwamy ze stosu
      S.pop;
      // Zapamiętujemy,
      // że nie ma go już na stosie
      VS[u] := false;
      // Zapamiętujemy numer
      // silnie spójnej składowej
      C[u] := v;
    // Kontynuujemy
    // aż do korzenia składowej
    until u = v;
  end;
end;

// Funkcja bada istnienie
// cyklu lub ścieżki Eulera
// Zwraca:
// 0 - brak cyklu i ścieżki
// 1 - jest ścieżka Eulera
// 2 - jest cykl Eulera
//-------------------------
function isEulerian : integer;
var
  v, cinout, coutin : integer;
begin
  // Zerujemy numer wierzchołka
  cvn := 0;
  // Przeglądamy kolejne wierzchołki
  for v := 0 to n-1 do
    // W wierzchołku nieodwiedzonym
    // uruchamiamy DFS
    if VN[v] = 0 then DFSscc(v);
  // Szukamy pierwszego
  // wierzchołka o niezerowym stopniu
  v := 0;
  while (v < n) and
        (Vind[v]+Voutd[v] = 0) do
    inc(v);
  // Graf jest pusty
  if v = n then Exit(0);
  // Zapamiętujemy numer
  // silnie spójnej składowej
  cvn := C[v];
  // Licznik wierzchołków
  // o większym o 1 stopniu wchodzącym
  cinout := 0;
  // Licznik wierzchołków
  // o większym o 1 stopniu wychodzącym
  coutin := 0;
  // Przeglądamy graf
  while v < n do
  begin
    if Vind[v]+Voutd[v] > 0 then
    begin
      // Graf nie jest silnie spójny
      if C[v] <> cvn then Exit(0);
      if Vind[v] <> Voutd[v] then
      begin
        if Vind[v]-Voutd[v] = 1 then
        begin
          inc(cinout);
          // Za dużo wierzchołków
          // o nierównych stopniach
          if cinout > 1 then Exit(0);
        end
        else if Voutd[v]-Vind[v] = 1 then
        begin
          inc(coutin);
          // Za dużo wierzchołków
          // o nierównych stopniach
          if coutin > 1 then Exit(0);
        end
        // Stopnie różnią się
        // o więcej niż 1
        else Exit(0);
      end;
    end;
    inc(v);
  end;
  // Analizujemy stan liczników
  if cinout = 1 then
    // Mamy ścieżkę Eulera
    Exit(1)
  else
    // Mamy cykl Eulera
    Exit(2);
end;

// **********************
// *** Program główny ***
// **********************

var
  i, v, u : integer;
  p, r : PSLel;

begin
  // Tworzymy pusty stos
  S.init;
  // Odczytujemy liczbę
  // wierzchołków i krawędzi
  read(n, m);
  // Tworzymy tablice dynamiczne
  SetLength(graf, n);
  SetLength(VN, n);
  SetLength(VS, n);
  SetLength(VLow, n);
  SetLength(Vind, n);
  SetLength(Voutd, n);
  SetLength(C, n);
  // Inicjujemy tablice
  for i := 0 to n-1 do
  begin
    graf[i] := nil;
    VN[i] := 0;
    Vind[i] := 0;
    Voutd[i] := 0;
    VS[i] := false;
  end;
  // Odczytujemy kolejne
  // definicje krawędzi.
  for i := 0 to m-1 do
  begin
    // Wierzchołki tworzące krawędź
    read(v, u);
    // Tworzymy nowy element
    new(p);
    // Numerujemy go jako u
    p^.v := u;
    // i dodajemy na początek
    // listy graf[v]
    p^.next := graf[v];
    graf[v] := p;
  end;
  writeln;
  // Badamy graf
  case isEulerian of
    0: writeln('NOT AN EULERIAN GRAPH');
    1: writeln('SEMI-EULERIAN GRAPH');
    2: writeln('EULERIAN GRAPH');
  end;
  // Usuwamy zmienne dynamiczne
  for i := 0 to n-1 do
  begin
    p := graf[i];
    while p <> nil do
    begin
      r := p;
      p := p^.next;
      dispose(r);
    end;
  end;
  SetLength(graf, 0);
  SetLength(VN, 0);
  SetLength(VLow, 0);
  SetLength(VS, 0);
  SetLength(Vind, 0);
  SetLength(Voutd, 0);
  SetLength(C, 0);
  S.destroy;
end.
C++
// Badanie istnienia
// cyklu lub ścieżki Eulera
// Data: 3.02.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------

#include <iostream>
#include <iomanip>

using namespace std;

// Typy dla dynamicznej
// tablicy list sąsiedztwa
// oraz stosu
struct SLel
{
  SLel * next;
  int v;
};

// Definicja typu
// obiektowego Stack
//------------------
class Stack
{
  private:
    // lista przechowująca stos
    SLel * S;

  public:
    Stack(); // konstruktor
    ~Stack(); // destruktor
    bool empty(void);
    int  top(void);
    void push(int v);
    void pop(void);
};

// Zmienne globalne
//-----------------
const int MAXINT = 2147483647;
int n,m,cvn;
int *VN,*VLow,*Vind,*Voutd,*C;
bool *VS;
Stack S;
SLel **graf;

//---------------------
// Metody obiektu Stack
//---------------------

// Konstruktor
//------------
Stack::Stack()
{
  S = nullptr;
}

// Destruktor
//-----------
Stack::~Stack()
{
  while(S) pop();
}

// Sprawdza, czy stos jest pusty
//------------------------------
bool Stack::empty(void)
{
  return !S;
}

// Zwraca szczyt stosu
//--------------------
int Stack::top (void)
{
  if(S)
    return S->v;
  else
    return -MAXINT;
}

// Zapisuje na stos
//-----------------
void Stack::push(int v)
{
  SLel * e = new SLel;
  e->v = v;
  e->next = S;
  S = e;
}

// Usuwa ze stosu
//---------------
void Stack::pop(void)
{
  if(S)
  {
    SLel * e = S;
    S = S->next;
    delete e;
  }
}

// Procedura wykonująca
// przejście DFS i wyznaczająca
// silnie spójną składową
// oraz stopnie wierzchołków
// v - wierzchołek startowy dla DFS
//---------------------------------
void DFSscc(int v)
{
  int u;
  SLel * p;

  // Ustalamy wstępnie parametr Low
  VN[v] = VLow[v] = ++cvn;
  // Wierzchołek na stos
  S.push(v);
  // Zapamiętujemy,
  // że v jest na stosie
  VS[v] = true;
  // Przeglądamy listę sąsiadów
  for(p = graf[v];p;p = p->next)
  {
    // Wyznaczamy stopień wychodzący
    ++Voutd[v];
    // i wchodzący
    ++Vind[p->v];
    // Jeśli sąsiad
    // jest nieodwiedzony, to
    if(!VN[p->v])
    {
      // wywołujemy dla niego
      // rekurencyjnie DFS
      DFSscc(p->v);
      // i obliczamy parametr Low dla v
      VLow[v] = min(VLow[v],VLow[p->v]);
    }
     // jeśli sąsiad odwiedzony,
     // lecz wciąż na stosie,
     else if(VS[p->v])
       // to wyznaczamy parametr Low dla v
       VLow[v] = min(VLow[v],VN[p->v]);
  }
  // Sprawdzamy,
  // czy mamy kompletną składową
  if(VLow[v] == VN[v])
    do
    {
      // Pobieramy wierzchołek
      // ze stosu
      u = S.top();
      // Pobrany wierzchołek
      // usuwamy ze stosu
      S.pop();
      // Zapamiętujemy,
      // że nie ma go już na stosie
      VS[u] = false;
      // Zapamiętujemy numer
      // silnie spójnej składowej
      C[u] = v;
    // Kontynuujemy
    // aż do korzenia składowej
    } while(u != v);
}

// Funkcja bada istnienie
// cyklu lub ścieżki Eulera
// Zwraca:
// 0 - brak cyklu i ścieżki
// 1 - jest ścieżka Eulera
// 2 - jest cykl Eulera
//-------------------------
int isEulerian()
{
  int v, cinout, coutin;

  // Zerujemy numer wierzchołka
  cvn = 0;
  // Przeglądamy kolejne wierzchołki
  for(v = 0; v < n; v++)
    // W wierzchołku nieodwiedzonym
    // uruchamiamy DFS
    if(!VN[v]) DFSscc(v);
  // Szukamy pierwszego wierzchołka
  // o niezerowym stopniu
  for(v = 0; (v < n) &&
             !(Vind[v] = Voutd[v]);
             v++);
  // Graf jest pusty
  if(v == n) return 0;
  // Zapamiętujemy
  // numer silnie spójnej składowej
  cvn = C[v];
  // Zerujemy liczniki
  cinout = coutin = 0;
  // Przeglądamy graf
  for(;v < n;v++)
    if(Vind[v] +
 Voutd[v])
    {
      // graf nie jest silnie spójny
      if(C[v] != cvn) return 0;
      if(Vind[v] != Voutd[v])
      {
        if(Vind[v]-Voutd[v] == 1)
        {
          // Za dużo wierzchołków
          // o nierównych stopniach
          if(++cinout > 1) return 0;
        }
        else if(Voutd[v]-Vind[v] == 1)
        {
          // Za dużo wierzchołków
          // o nierównych stopniach
          if(++coutin > 1) return 0;
        }
        // Stopnie różnią się
        // o więcej niż 1
        else return 0;
      }
    }
  // Analizujemy stan liczników
  //---------------------------
  // Mamy ścieżkę Eulera
  if(cinout == 1) return 1;
  // Mamy cykl Eulera
  else return 2;
}

// **********************
// *** Program główny ***
// **********************

int main()
{
  int i, v, u;
  SLel *p, *r;

  // Odczytujemy liczbę
  // wierzchołków i krawędzi
  cin >> n >> m;
  // Tworzymy tablice dynamiczne
  graf = new SLel * [n];
  VN = new int [n];
  VS = new bool [n];
  VLow = new int [n];
  Vind = new int [n];
  Voutd = new int [n];
  C = new int [n];
  // Inicjujemy tablice
  for(i = 0; i < n; i++)
  {
    graf[i] = nullptr;
    VN[i] = Vind[i] = Voutd[i] = 0;
    VS[i] = false;
  }
  // Odczytujemy kolejne
  // definicje krawędzi.
  for(i = 0; i < m; i++)
  {
    // Wierzchołki tworzące krawędź
    cin >> v >> u;
    // Tworzymy nowy element
    p = new SLel;
    // Numerujemy go jako u
    p->v = u;
    // i dodajemy na początek
    // listy graf[v]
    p->next = graf[v];
    graf[v] = p;
  }
  cout << endl;
  // Badamy graf
  switch(isEulerian())
  {
    case 0:
      cout << "NOT AN EULERIAN GRAPH\n";
      break;
    case 1:
      cout << "SEMI-EULERIAN GRAPH\n";
      break;
    case 2:
      cout << "EULERIAN GRAPH\n";
      break;
  }
  // Usuwamy zmienne dynamiczne
  for(i = 0; i < n; i++)
  {
    p = graf[i];
    while(p)
    {
      r = p;
      p = p->next;
      delete r;
    }
  }
  delete [] graf;
  delete [] VN;
  delete [] VLow;
  delete [] VS;
  delete [] Vind;
  delete [] Voutd;
  delete [] C;
  return 0;
}
Basic
' Badanie istnienia
' cyklu lub ścieżki Eulera
' Data: 3.02.2014
' (C)2014 mgr Jerzy Wałaszek
'---------------------------

' Typy dla dynamicznej
' tablicy list sąsiedztwa
' oraz stosu
Type SLel
  next As SLel Ptr
  v As Integer
End Type

Type Stack
  Private:
    ' lista zawierająca stos
    S As SLel Ptr
  Public:
    Declare Constructor
    Declare Destructor
    Declare Function empty As Integer
    Declare Function top As Integer
    Declare Sub push(ByVal v As Integer)
    Declare Sub pop
End Type

' Zmienne globalne
'-----------------
Const MAXINT = 2147483647
Dim Shared As Integer n, m, cvn
Dim Shared As Integer Ptr VN, VLow, _
                          Vind, Voutd, C
Dim Shared As Byte Ptr VS
Dim Shared As Stack S
Dim Shared As SLel Ptr Ptr graf

'---------------------
' Metody obiektu Stack
'---------------------

' Konstruktor
'------------
Constructor Stack
  S = 0
End Constructor

' Destruktor
'-----------
Destructor Stack
  While S <> 0
    pop
  Wend
End Destructor

' Sprawdza, czy stos jest pusty
'------------------------------
Function Stack.empty As Integer
  If S = 0 Then Return 1
  Return 0
End Function

' Zwraca szczyt stosu
'--------------------
Function Stack.top As Integer
  If S <> 0 Then
    top = S->v
  Else
    top = -MAXINT
  End If
End Function

' Zapisuje na stos
'-----------------
Sub Stack.push(ByVal v As Integer)
  Dim e As SLel Ptr
  e = New SLel
  e->v = v
  e->next = S
  S = e
End Sub

' Usuwa ze stosu
'---------------
Sub Stack.pop
  Dim e As SLel Ptr
  If S <> 0 Then
    e = S
    S = S->Next
    Delete e
  End If
End Sub

' Funkcja zwraca
' mniejszą z liczb a i b
'-----------------------
Function min(ByVal a As Integer, _
             ByVal b As Integer) _
                     As Integer
  If a < b Then
    Return a
  Else
    Return b
  EndIf
End Function

' Procedura wykonująca
' przejście DFS i wyznaczająca
' silnie spójną składową
' oraz stopnie wierzchołków
' v - wierzchołek startowy dla DFS
'---------------------------------
sub DFSscc(ByVal v As Integer)

  Dim As Integer u
  Dim As SLel Ptr p

  cvn += 1
  VN[v] = cvn
  ' Ustalamy wstępnie parametr Low
  VLow[v] = cvn
  ' Wierzchołek na stos
  S.push(v)
  ' Zapamiętujemy,
  ' że v jest na stosie
  VS[v] = 1
  p = graf[v]
  ' Przeglądamy listę sąsiadów
  While p <> 0
    ' Wyznaczamy stopień wychodzący
    Voutd[v] += 1
    ' i wchodzący
    Vind[p->v] += 1
    ' Jeśli sąsiad jest
    ' nieodwiedzony, to
    If VN[p->v] = 0 Then
      ' wywołujemy dla niego
      ' rekurencyjnie DFS
      DFSscc(p->v)
      ' i obliczamy parametr Low dla v
      VLow[v] = min(VLow[v],VLow[p->v])
    ' Jeśli sąsiad odwiedzony,
    ' lecz wciąż na stosie,
    ElseIf VS[p->v] = 1 Then
      ' to wyznaczamy parametr
      ' Low dla v
      VLow[v] = min(VLow[v],VN[p->v])
    End If
    p = p->next
  Wend
  ' Sprawdzamy,
  ' czy mamy kompletną składową
  If VLow[v] = VN[v] Then
    Do
      ' Pobieramy wierzchołek ze stosu
      u = S.top
      ' Pobrany wierzchołek
      ' usuwamy ze stosu
      S.pop
      ' Zapamiętujemy,
      ' że nie ma go już na stosie
      VS[u] = 0
      ' Zapamiętujemy numer
      ' silnie spójnej składowej
      C[u] = v
    ' Kontynuujemy
    ' aż do korzenia składowej
    Loop While u <> v
  End If
End Sub

' Funkcja bada istnienie
' cyklu lub ścieżki Eulera
' Zwraca:
' 0 - brak cyklu i ścieżki
' 1 - jest ścieżka Eulera
' 2 - jest cykl Eulera
'-------------------------
Function isEulerian As Integer
  Dim As Integer v, cinout, coutin

  ' Zerujemy numer wierzchołka
  cvn = 0
  ' Przeglądamy kolejne wierzchołki
  For v = 0 To n-1
    ' W wierzchołku nieodwiedzonym
    ' uruchamiamy DFS
    If VN[v] = 0 then DFSscc(v)
  Next
   ' Szukamy pierwszego wierzchołka
   ' o niezerowym stopniu
  v = 0
  While (v < n) AndAlso _
        (Vind[v]+Voutd[v] = 0)
    v += 1
  Wend
  ' Graf jest pusty
  If v = n then Return 0
  ' Zapamiętujemy numer
  ' silnie spójnej składowej
  cvn = C[v]
  ' Zerujemy liczniki
  cinout = 0
  coutin = 0
  ' Przeglądamy graf
  While v < n
    If Vind[v] = Voutd[v] > 0 Then
      ' graf nie jest silnie spójny
      If C[v] <> cvn Then Return 0
      If Vind[v] <> Voutd[v] Then
        If Vind[v]-Voutd[v] = 1 Then
          cinout += 1
          ' Za dużo wierzchołków
          ' o nierównych stopniach
          If cinout > 1 then return 0
        ElseIf Voutd[v]-Vind[v] = 1 Then
          coutin += 1
          ' Za dużo wierzchołków
          ' o nierównych stopniach
          If coutin > 1 Then Return 0
        Else
          ' Stopnie różnią się
          ' o więcej niż 1
          Return 0
        End If
      End If
    End If
    v += 1
  Wend
  ' Analizujemy stan liczników
  '---------------------------
  ' Mamy ścieżkę Eulera
  If cinout = 1 Then Return 1
  ' mamy cykl Eulera
  return 2
End Function

' **********************
' *** Program główny ***
' **********************

Dim As Integer i, v, u
Dim As SLel Ptr p, r

Open Cons For Input As #1
' Odczytujemy liczbę
' wierzchołków i krawędzi
Input #1, n, m
' Tworzymy tablice dynamiczne
graf = New SLel Ptr [n]
VN = New Integer [n]
VS = New Byte [n]
VLow = New Integer [n]
Vind = New Integer [n]
Voutd = New Integer [n]
C = New Integer [n]
' Inicjujemy tablice
For i = 0 To n-1
  graf[i]  = 0
  VN[i]    = 0
  Vind[i]  = 0
  Voutd[i] = 0
  VS[i]    = 0
Next
' Odczytujemy kolejne
' definicje krawędzi.
For i = 1 To m
  ' Wierzchołki tworzące krawędź
  Input #1, v, u
  ' Tworzymy nowy element
  p = New SLel
  ' Numerujemy go jako w
  p->v = u
  ' Dodajemy go
  ' na początek listy graf[v]
  p->next = graf[v]
  graf[v] = p
Next
Close #1
Print
' Badamy graf
Select Case isEulerian
  Case 0
    Print "NOT AN EULERIAN GRAPH"
  Case 1
    Print "SEMI-EULERIAN GRAPH"
  Case 2
    Print "EULERIAN GRAPH"
End Select
' Usuwamy tablice dynamiczne
For i = 0 To n-1
  p = graf[i]
  While p <> 0
    r = p
    p = p->next
    Delete r
  Wend
Next
Delete [] graf
Delete [] VN
Delete [] VLow
Delete [] VS
Delete [] Vind
Delete [] Voutd
Delete [] C
End
Python (dodatek)
# Badanie istnienia
# cyklu lub ścieżki Eulera
# Data: 3.02.2014
# (C)2014 mgr Jerzy Wałaszek
#---------------------------

# Klasy dla dynamicznej
# tablicy list sąsiedztwa
# oraz stosu
class SLel:
    def __init__(self):
        self.next = None
        self.v = 0

MAXINT = 2147483647

class Stack:
    # Konstruktor
    def __init__(self):
        self.s = None


    # Destruktor
    def __del__(self):
        while self.s:
            self.pop()


    # Sprawdza,
    # czy stos jest pusty
    def empty(self):
        return not self.s


    # Zwraca szczyt stosu
    def top(self):
        global MAXINT
        if self.s:
            return self.s.v
        else:
            return -MAXINT


    # Zapisuje na stos
    def push(self, v):
        e = SLel()
        e.v = v
        e.next = self.s
        self.s = e


    # Usuwa ze stosu
    def pop(self):
        if self.s:
            self.s = self.s.next


# Procedura wykonująca
# przejście DFS i wyznaczająca
# silnie spójną składową
# oraz stopnie wierzchołków
# v - wierzchołek startowy dla DFS
def dfs_scc(v):
    global cvn,vn,vlow,s,vs,graf
    global voutd,vind,c
    cvn += 1
    vn[v] = cvn
    # Ustalamy wstępnie parametr Low
    vlow[v] = cvn
    # Wierzchołek na stos
    s.push(v)
    # Zapamiętujemy,
    # że v jest na stosie
    vs[v] = True
    p = graf[v]
    # Przeglądamy listę sąsiadów
    while p:
        # Wyznaczamy stopień wychodzący
        voutd[v] += 1
        # i wchodzący
        vind[p.v] += 1
        # Jeśli sąsiad jest
        # nieodwiedzony, to
        if not vn[p.v]:
            # wywołujemy dla niego
            # rekurencyjnie DFS
            dfs_scc(p.v)
            # i obliczamy parametr Low dla v
            vlow[v] = min(vlow[v],vlow[p.v])
        # Jeśli sąsiad odwiedzony,
        # lecz wciąż na stosie,
        elif vs[p.v]:
            # to wyznaczamy parametr
            # Low dla v
            vlow[v] = min(vlow[v],vn[p.v])
        p = p.next
    # Sprawdzamy,
    # czy mamy kompletną składową
    if vlow[v] == vn[v]:
        while True:
            # Pobieramy wierzchołek ze stosu
            u = s.top()
            # Pobrany wierzchołek
            # usuwamy ze stosu
            s.pop()
            # Zapamiętujemy,
            # że nie ma go już na stosie
            vs[u] = False
            # Zapamiętujemy numer
            # silnie spójnej składowej
            c[u] = v
            # Kontynuujemy
            # aż do korzenia składowej
            if u == v: break


# Funkcja bada istnienie
# cyklu lub ścieżki Eulera
# Zwraca:
# 0 - brak cyklu i ścieżki
# 1 - jest ścieżka Eulera
# 2 - jest cykl Eulera
def is_eulerian():
    global cvn,n,vn,vind,voutd,c
    # Zerujemy numer wierzchołka
    cvn = 0
    # Przeglądamy kolejne wierzchołki
    for v in range(n):
        # W wierzchołku nieodwiedzonym
        # uruchamiamy DFS
        if not vn[v]: dfs_scc(v)
     # Szukamy pierwszego wierzchołka
     # o niezerowym stopniu
    v = 0
    while (v < n) and \
          (vind[v]+voutd[v] == 0):
        v += 1
    # Graf jest pusty
    if v == n: return 0
    # Zapamiętujemy numer
    # silnie spójnej składowej
    cvn = c[v]
    # Zerujemy liczniki
    cinout = 0
    coutin = 0
    # Przeglądamy graf
    while v < n:
        if vind[v]+voutd[v] > 0:
            # graf nie jest silnie spójny
            if c[v] != cvn: return 0
            if vind[v] != voutd[v]:
                if vind[v]-voutd[v] == 1:
                    cinout += 1
                    # Za dużo wierzchołków
                    # o nierównych stopniach
                    if cinout > 1: return 0
                elif voutd[v]-vind[v] == 1:
                    coutin += 1
                    # Za dużo wierzchołków
                    # o nierównych stopniach
                    if coutin > 1: return 0
                else:
                    # Stopnie różnią się
                    # o więcej niż 1
                    return 0
        v += 1
    # Analizujemy stan liczników
    #---------------------------
    # Mamy ścieżkę Eulera
    if cinout == 1: return 1
    # mamy cykl Eulera
    return 2


# **********************
# *** Program główny ***
# **********************

cvn = 0
s = Stack()
# Odczytujemy liczbę
# wierzchołków i krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy tablice dynamiczne
graf = [None] * n
vn = [0] * n
vs = [False] * n
vlow = [0] * n
vind = [0] * n
voutd = [0] * n
c = [0] * n
# Odczytujemy kolejne
# definicje krawędzi.
for i in range(m):
    # Wierzchołki tworzące krawędź
    x = input().split()
    v = int(x[0])
    u = int(x[1])
    # Tworzymy nowy element
    p = SLel()
    # Numerujemy go jako w
    p.v = u
    # Dodajemy go na początek listy graf[v]
    p.next = graf[v]
    graf[v] = p
print()
# Badamy graf
match is_eulerian():
    case 0:
        print("NOT AN EULERIAN GRAPH")
    case 1:
        print("SEMI-EULERIAN GRAPH")
    case 2:
        print("EULERIAN GRAPH")
# Usuwamy tablice dynamiczne
for i in range(n):
    while graf[i]:
        graf[i] = graf[i].next
del graf,vn,vlow,vs,vind,voutd,c
Wynik:
9 17
0 1
1 3
1 4
2 1
2 5
3 0
3 2
3 7
4 2
4 3
4 6
5 4
5 7
6 3
7 4
7 8
8 5

EULERIAN GRAPH
  9 17
0 1
0 7
1 2
1 5
2 3
2 4
3 0
3 7
4 5
4 8
5 2
5 7
6 3
7 4
7 6
7 8
8 1

SEMI-EULERIAN GRAPH
  9 10
0 4
1 5
1 6
2 7
3 1
4 2
5 8
6 3
7 0
8 1

NOT AN EULERIAN GRAPH

do podrozdziału  do strony 

Zespół Przedmiotowy
Chemii-Fizyki-Informatyki

w I Liceum Ogólnokształcącym
im. Kazimierza Brodzińskiego
w Tarnowie
ul. Piłsudskiego 4
©2026 mgr Jerzy Wałaszek

Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.