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

Znajdowanie klik w grafie

SPIS TREŚCI
Tematy pomocnicze
Podrozdziały

Problem

W danym grafie nieskierowanym należy znaleźć wszystkie kliki maksymalne.

Kliką (ang. clique) w grafie nazwiemy jego podgraf, którego wszystkie wierzchołki są ze sobą nawzajem połączone krawędziami. Innymi słowy, klika jest podgrafem pełnym danego grafu.
obrazek

Klika maksymalna (ang. maximal clique) jest taką kliką grafu, że nie można do niej już dodać żadnego nowego wierzchołka z grafu.
Klika największa (ang. maximum clique) jest największym podgrafem pełnym. Problem znajdowania największej kliki jest problemem trudnym obliczeniowo.

Rozwiązanie nr 1

Do znajdowania maksymalnych klik w grafie nieskierowanym można zastosować prosty algorytm rekurencyjny Brona-Kerboscha. Algorytm otrzymuje na wejściu trzy zbiory P, R i X o następującym znaczeniu:

P : zbiór wierzchołków, które są kandydatami do  rozważenia.
R : zbiór wierzchołków, które są częściowym wynikiem znajdowania kliki.
X : zbiór wierzchołków pominiętych.

Przy pierwszym wywołaniu algorytmu zbiory R i X są puste, a zbiór P zawiera wszystkie wierzchołki grafu. Zasada działania algorytmu jest następująca:

Najpierw sprawdzamy w algorytmie, czy zbiory P i X są puste. Jeśli tak, to zbiór R zawiera maksymalną klikę. Wypisujemy zawartość zbioru R i kończymy.

Jeśli zbiór P nie jest pusty, to wybieramy z niego kolejne wierzchołki v. Dla każdego z tych wierzchołków tworzymy następujące zbiory tymczasowe:

  • N – zbiór wszystkich sąsiadów wierzchołka v
  • R' – zbiór R z dodanym wierzchołkiem v
  • P' – iloczyn zbiorów P i N (z P' zostaną usunięte wszystkie wierzchołki, które nie są sąsiadami wierzchołka v)
  • X' – iloczyn zbiorów X i N (z X' zostaną usunięte wszystkie wierzchołki, które nie są sąsiadami wierzchołka v)

Wywołujemy rekurencyjnie algorytm ze zbiorami P',R'X'.

Następnie ze zbioru P usuwamy wierzchołek v, a dodajemy go do zbioru X i kontynuujemy pętlę.

Algorytm Brona-Kerboscha znajdowania wszystkich klik maksymalnych w grafie nieskierowanym - wersja nr 1

BronKerbosch(n,graf,R,P,X)

Wejście:

n : liczba wierzchołków w grafie, n ∈ C.
graf : zadany w dowolnie wybrany sposób, algorytm tego nie precyzuje.
R : zbiór wierzchołków, które są częściowym wynikiem znajdowania kliki.
P : zbiór wierzchołków, które są kandydatami do rozważenia.
X : zbiór wierzchołków pominiętych.

Wyjście:

Wszystkie kliki maksymalne w grafie

Elementy pomocnicze:

u,v : numery wierzchołków grafu; u,v ∈ C.
P',R',X',N : pomocnicze zbiory wierzchołków.

Lista kroków:

K01: Jeśli (P jest pusty) obrazek (X jest pusty), ; znaleziona klika maksymalna
     to Pisz R
     i zakończ
K02: Dla każdego wierzchołka v w P:
     wykonuj kroki K03…K10
K03:   Zeruj zbiór N
K04:   Dla każdego sąsiada u wierzchołka v: ; tworzymy zbiór sąsiadów
       wykonuj: NN + u
K05:   R' ← R+v ; w R' tworzymy zbiór R z dodanym wierzchołkiem v
K06:   P' ← iloczyn zbiorów P i N ; z P' usuwamy wierzchołki, które nie są sąsiadami v
K07:   X' ← iloczyn zbiorów X i N ; z X' usuwamy wierzchołki, które nie są sąsiadami v
K08:   BronKerbosch(n,graf,R',P',X') ; wywołanie rekurencyjne
K09:   PP - v ; ze zbioru P usuwamy wierzchołek v
K10:   RR + v ; do zbioru R dodajemy wierzchołek v
K11: Zakończ

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 wyszukuje w nim wszystkie kliki maksymalne za pomocą algorytmu Brona-Kerboscha. Zbiory zostały zaimplementowane w tablicy logicznej.
Program dodatkowo wypisuje liczbę wywołań rekurencyjnych algorytmu Brona-Kerboscha dla celów porównawczych.
Dane przykładowe (przekopiuj do schowka i wklej do okna konsoli):
obrazek 8 19
0 1
0 2
0 5
0 6
1 2
1 3
1 5
1 6
2 4
2 5
2 6
2 7
3 4
3 6
3 7
4 6
5 6
5 7
6 7
Pascal
// Algorytm Brona-Kerboscha
// Data: 10.07.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------

program cliques;

// Definicja elementu
// listy sąsiedztwa
type
  PSLel = ^SLel;
  SLel = record
    // Następny
    // element listy
    next : PSLel;
    // Wierzchołek docelowy
    v : integer;
  end;

// Definicja struktury zbioru
  SSet = record
    vmin : integer;
    nmax : integer;
    T : array of boolean;
  end;

// Zmienne globalne
var
  // Liczba wierzchołków
  // i krawędzi grafu
  n, m : integer;
  // Tablica list
  // sąsiedztwa
  graf : array of PSLel;
  // Zbiór sąsiadów
  SN : SSet;
  // Licznik wywołań
  // rekurencyjnych
  rcnt : integer;

// Obsługa zbiorów
// ---------------

// Procedura dodaje
// element x do zbioru A
//----------------------
procedure s_add(var A : SSet;
                    x : integer);
var
  i : integer;
begin
  // Obliczamy indeks
  i := x - A.vmin;
  // Ustawiamy element
  A.T[i] := true;
end;

// Procedura usuwa
// element x ze zbioru
//--------------------
procedure s_remove(var A : SSet;
                       x : integer);
var
  i : integer;
begin
  // Obliczamy indeks
  i := x - A.vmin;
  // Zerujemy element
  A.T[i] := false;
end;

// Procedura usuwa
// wszystkie elementy
// ze zbioru
//-------------------
procedure s_clear(var A : SSet);
var
  i : integer;
begin
  // Zerujemy tablicę
  for i := 0 to A.nmax do
    A.T[i] := false;
end;

// Procedura tworzy zbiór
//-----------------------
procedure s_create(var A : SSet;
              vmin, vmax : integer);
begin
  // Wypełniamy
  // strukturę danymi
  A.vmin := vmin;
  // Indeks ostatniego
  // elementu tablicy
  A.nmax := vmax - vmin;
  // Tworzymy dynamicznie
  // tablicę logiczną
  SetLength(A.T, A.nmax + 1);
  // Tworzymy zbiór pusty
  s_clear(A);
end;

// Procedura wyznacza
// część wspólną zbiorów A i B
//----------------------------
procedure s_intersection
          (var A, B, C :SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    C.T[i] := A.T[i] and B.T[i];
end;

// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
procedure s_copy(var A, B : SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    B.T[i] := A.T[i];
end;

// Procedura ustawia
// zbiór pełny
procedure s_full(var A : SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    A.T[i] := true;
end;

// Funkcja sprawdza,
// czy zbiór A jest pusty
// true  - jest
// false - nie jest
//-----------------------
function s_empty(var A : SSet)
                       : boolean;
var
  i : integer;
  m : boolean;
begin
  // Pobieramy bity do m
  m := A.T[0];
  for i := 1 to A.nmax do
    // Sumujemy logicznie bity
    m := m or A.T[i];
  s_empty := (m = false);
end;

// Funkcja bada,
// czy element x
// należy do zbioru A
// true  - tak, należy
// false - nie, nie należy
//------------------------
function s_isin(var A : SSet;
                    x : integer)
                      : boolean;
var
  i : integer;
begin
  // Obliczamy indeks
  i := x - A.vmin;
  s_isin := A.T[i]
end;

// Procedura wyświetla
// elementy zbioru A
//--------------------
procedure s_print(var A : SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    if A.T[i] then
        write(i + A.vmin,' ');
end;

// Procedura wyszukuje
// kliki maksymalne
//--------------------
procedure BronKerbosch
          (var SR, SP, SX : SSet);
var
  RP, PP, XP : SSet;
  p : PSLel;
  v : integer;
begin
  // Zwiększamy licznik
  // wywołań rekurencyjnych
  inc(rcnt);
  // Jeśli zbiory SP i SX są puste,
  if s_empty(SP) and
     s_empty(SX) then
  // to SR zawiera wierzchołki
  // kliki maksymalnej
  begin
    write('MAXIMAL CLIQUE: ');
    s_print(SR);
    writeln;
  end
  else
  begin
    // Tworzymy puste
    // zbiory pomocnicze
    s_create(RP, 0, n-1);
    s_create(PP, 0, n-1);
    s_create(XP, 0, n-1);
    // Przechodzimy przez
    // kolejne wierzchołki
    // grafu
    for v := 0 to n - 1 do
      // Jeśli wierzchołek
      // v jest w zbiorze SP, to
      if s_isin(SP, v) then
      // go przetwarzamy
      begin
        // Najpierw zerujemy
        // zbiór sąsiadów
        s_clear(SN);
        // Przeglądamy listę
        // sąsiedztwa wierzchołka v
        p := graf[v];
        while p <> nil do
        begin
          // Każdego sąsiada
          // umieszczamy w SN
          s_add(SN, p^.v);
          p := p^.next;
        end;
        // W RP tworzymy zbiór SR
        // z dodanym wierzchołkiem v
        s_copy(SR, RP);
        s_add(RP, v);
        // W PP tworzymy
        // iloczyn SP i SN
        s_intersection(SP, SN, PP);
        // W XP tworzymy
        // iloczyn SX i SN
        s_intersection(SX, SN, XP);
        // Wywołanie rekurencyjne
        BronKerbosch(RP, PP, XP);
        // Z SP usuwamy
        // wierzchołek v
        s_remove(SP, v);
        // i dodajemy go do SX
        s_add(SX, v);
      end;
   // Usuwamy zbiory pomocnicze
   SetLength(RP.T, 0);
   SetLength(PP.T, 0);
   SetLength(XP.T, 0);
  end;
end;

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

var
  SR, SP, SX :SSet;
  i, u, v : integer;
  p, r : PSLel;
begin
  // Odczytujemy liczbę
  // wierzchołków i krawędzi
  read(n, m);
  // Tworzymy puste zbiory
  s_create(SR, 0, n-1);
  s_create(SP, 0, n-1);
  s_create(SX, 0, n-1);
  s_create(SN, 0, n-1);
  // Tworzymy tablicę
  // list sąsiedztwa
  SetLength(graf, n);
  // Zerujemy listy sąsiedztwa
  for i := 0 to n - 1 do
    graf[i] := nil;
  for i := 1 to m do
  begin
    // Wierzchołki krawędzi
    read(u, v);
    // Tworzymy element listy
    new(p);
    p^.v := u;
    // Element dołączamy
    // do listy sąsiadów v
    p^.next := graf[v];
    graf[v] := p;
    // To samo dla krawędzi
    // w drugą stronę
    new(p);
    p^.v := v;
    // Element dołączamy
    // do listy sąsiadów u
    p^.next := graf[u];
    graf[u] := p;
  end;
  // W zbiorze SP ustawiamy
  // wszystkie wierzchołki
  s_full(SP);
  writeln;
  // Zerujemy licznik
  // wywołań rekurencyjnych
  rcnt := 0;
  // Szukamy rekurencyjnie
  // klik maksymalnych
  BronKerbosch(SR, SP, SX);
  writeln;
  writeln('RECURSIVE CALLS = ',rcnt);
  writeln;
  // Usuwamy zbiory
  SetLength(SR.T, 0);
  SetLength(SP.T, 0);
  SetLength(SX.T, 0);
  SetLength(SN.T, 0);
  // Usuwamy tablicę
  // list sąsiedztwa
  for v := 0 to n-1 do
  begin
    p := graf[v];
    while p <> nil do
    begin
      r := p;
      p := p^.next;
      dispose(r);
    end;
  end;
  SetLength(graf, 0);
end.
C++
// Algorytm Brona-Kerboscha
// Data: 10.07.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------

#include <iostream>
#include <iomanip>

using namespace std;

// Definicja elementu
// listy sąsiedztwa
struct SLel
{
  // Następny element listy
  SLel *next;
  // Wierzchołek docelowy
  int v;
};

// Definicja struktury zbioru
struct Set
{
  int vmin, nmax;
  bool *T;
};

// Zmienne globalne
//-----------------
// Liczba wierzchołków
// i krawędzi grafu
int n, m;
// Tablica list sąsiedztwa
SLel ** graf;
// Zbiór sąsiadów
Set SN;
// Licznik wywołań
// rekurencyjnych
int rcnt = 0;

// Obsługa zbiorów
//----------------

// Procedura dodaje
// element x do zbioru A
//----------------------
void s_add(Set & A, int x)
{
  // Obliczamy indeks
  int i = x - A.vmin;
  // Ustawiamy bit na 1
  A.T[i] = true;
}

// Procedura usuwa
// element x ze zbioru
//--------------------
void s_remove(Set & A, int x)
{
  // Obliczamy indeks
  int i = x - A.vmin;
  // Ustawiamy bit na 0
  A.T[i] = false;
}

// Procedura usuwa wszystkie
// elementy ze zbioru
//--------------------------
void s_clear(Set & A)
{
  // Zerujemy tablicę
  for(int i = 0; i <= A.nmax; i++)
    A.T[i] = false;
}

// Procedura tworzy zbiór
//-----------------------
void s_create(Set & A,
              int vmin,
              int vmax)
{
  // Wypełniamy strukturę danymi
  A.vmin = vmin;
  // Indeks ostatniego
  // elementu tablicy
  A.nmax = vmax - vmin;
  // Tworzymy dynamicznie
  // tablicę logiczną
  A.T = new bool [A.nmax+1];
  // Tworzymy zbiór pusty
  s_clear(A);
}

// Procedura wyznacza część
// wspólną zbiorów A i B
//-------------------------
void s_intersection(Set & A,
                    Set & B,
                    Set & C)
{
  for(int i = 0; i <= A.nmax; i++)
    C.T[i] = A.T[i] && B.T[i];
}

// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
void s_copy(Set & A, Set & B)
{
  for(int i = 0; i <= A.nmax; i++)
    B.T[i] = A.T[i];
}

// Procedura ustawia
// zbiór pełny
//-------------------
void s_full(Set & A)
{
  for(int i = 0; i <= A.nmax; i++)
    A.T[i] = true;
}

// Funkcja sprawdza,
// czy zbiór A jest pusty
// true  - tak, jest pusty
// false - nie, nie jest pusty
//----------------------------
bool s_empty(Set A)
{
  // Pobieramy bity do m
  bool m = A.T[0];
  // Sumujemy logicznie bity
  for(int i = 1; i <= A.nmax; i++)
    m = m || A.T[i];
  return !m;
}

// Funkcja bada, czy element
// x należy do zbioru A
// true  - tak, należy
// false - nie, nie należy
//--------------------------
bool s_isin(Set & A, int x)
{
  // Obliczamy indeks
  int i = x - A.vmin;
  return A.T[i];
}

// Procedura wyświetla
// elementy zbioru A
//--------------------
void s_print(Set A)
{
  int i;

  for(i = 0; i <= A.nmax; i++)
    if(A.T[i])
      cout << i + A.vmin << " ";
}

// Procedura wyszukuje
// kliki maksymalne
//--------------------
void BronKerbosch(Set & SR,
                  Set & SP,
                  Set & SX)
{
  Set RP, PP, XP;
  SLel * p;
  int v;

  // Zwiększamy licznik
  // wywołań rekurencyjnych
  rcnt++;
  // Jeśli zbiory SP
  // i SX są puste,
  if(s_empty(SP) && s_empty(SX))
  // to SR zawiera wierzchołki
  // kliki maksymalnej
  {
    cout << "MAXIMAL CLIQUE: ";
    s_print(SR);
    cout << endl;
  }
  else
  {
    // Tworzymy puste
    // zbiory pomocnicze
    s_create(RP, 0, n-1);
    s_create(PP, 0, n-1);
    s_create(XP, 0, n-1);
    // Przechodzimy przez
    // kolejne wierzchołki grafu
    for(v = 0; v < n; v++)
      // Jeśli wierzchołek v
      // jest w zbiorze SP, to
      if(s_isin(SP, v))
      // go przetwarzamy
      {
        // Najpierw zerujemy
        // zbiór sąsiadów
        s_clear(SN);
        // Przeglądamy listę
        // sąsiedztwa wierzchołka v
        for(p = graf[v];p;p = p->next)
          // Każdego sąsiada
          // umieszczamy w SN
          s_add(SN, p->v);
        // W RP tworzymy zbiór SR
        // z dodanym wierzchołkiem v
        s_copy(SR, RP);
        s_add(RP, v);
        // W PP tworzymy
        // iloczyn SP i SN
        s_intersection(SP, SN, PP);
        // W XP tworzymy
        // iloczyn SX i SN
        s_intersection(SX, SN, XP);
        // Wywołanie rekurencyjne
        BronKerbosch (RP, PP, XP);
        // Z SP usuwamy
        // wierzchołek v
        s_remove(SP, v);
        // i dodajemy go do SX
        s_add(SX, v);
      }
    // Usuwamy zbiory pomocnicze
    delete [] RP.T;
    delete [] PP.T;
    delete [] XP.T;
  }
}

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

int main()
{
  Set SR, SP, SX;
  int i, u, v;
  SLel *p, *r;

  // Odczytujemy liczbę
  // wierzchołków i krawędzi
  cin >> n >> m;
  // Tworzymy puste zbiory
  s_create(SR, 0, n-1);
  s_create(SP, 0, n-1);
  s_create(SX, 0, n-1);
  s_create(SN, 0, n-1);
  // Tworzymy tablicę
  // list sąsiedztwa
  graf = new SLel * [n];
  // Zerujemy listy sąsiedztwa
  for(i = 0; i < n; i++)
    graf[i] = nullptr;
  for(i = 0; i < m; i++)
  {
    // Wierzchołki krawędzi
    cin >> u >> v;
    // Tworzymy element listy
    p = new SLel;
    p->v = u;
    // Element dołączamy
    // do listy sąsiadów v
    p->next = graf[v];
    graf[v] = p;
    p = new SLel;
    p->v = v;
    // Element dołączamy
    // do listy sąsiadów u
    p->next = graf[u];
    graf[u] = p;
  }
  // W zbiorze SP ustawiamy
  // wszystkie wierzchołki
  s_full(SP);
  cout << endl;
  // Szukamy rekurencyjnie
  // klik maksymalnych
  BronKerbosch(SR, SP, SX);
  cout << endl
       << "RECURSIVE CALLS = "
       << rcnt << endl << endl;
  // Usuwamy zbiory
  delete [] SR.T;
  delete [] SP.T;
  delete [] SX.T;
  delete [] SN.T;
  // Usuwamy tablicę
  // list sąsiedztwa
  for(v = 0; v < n; v++)
  {
    p = graf[v];
    while(p)
    {
      r = p;
      p = p->next;
      delete r;
    }
  }
  delete [] graf;
  return 0;
}
Basic
' Algorytm Brona-Kerboscha
' Data: 10.07.2014
' (C)2014 mgr Jerzy Wałaszek
'---------------------------

' Definicja elementu
' listy sąsiedztwa
Type SLel
  ' Następny element listy
  next As SLel Ptr
  ' Wierzchołek docelowy
  v As Integer
End Type

' Definicja struktury zbioru
Type Set
  vmin As Integer
  nmax As Integer
  T As Boolean Ptr
End Type

' Zmienne globalne
'-----------------
' Liczba wierzchołków
' i krawędzi grafu
Dim Shared As Integer n, m
' Tablica list sąsiedztwa
Dim Shared As SLel Ptr Ptr graf
' Zbiór sąsiadów
Dim Shared As Set SN
' Licznik wywołań rekurencyjnych
Dim Shared As Integer rcnt = 0

' Obsługa zbiorów
'----------------
' Procedura dodaje
' element x do zbioru A
'----------------------
Sub s_add(ByRef A As Set, _
          ByVal x As Integer)
  Dim As Integer i

  ' Obliczamy indeks
  i = x - A.vmin
  ' Ustawiamy bit na 1
  A.T[i] = True
End Sub

' Procedura usuwa
' element x ze zbioru
'--------------------
Sub s_remove(ByRef A As Set, _
             ByVal x As Integer)
  Dim As Integer i

  ' Obliczamy indeks
  i = x - A.vmin
  ' Ustawiamy bit na 0
  A.T[i] = False
End Sub

' Procedura usuwa
' wszystkie elementy
' ze zbioru
'-------------------
Sub s_clear(ByRef A As Set)
  Dim As Integer i
  
  ' Zerujemy tablicę
  For i = 0 To A.nmax
    A.T[i] = False
  Next
End Sub

' Procedura tworzy zbiór
'-----------------------
Sub s_create(ByRef A As Set, _
             ByVal vmin As Integer, _
             ByVal vmax As Integer)
  ' Wypełniamy strukturę danymi
  A.vmin = vmin
  ' Indeks ostatniego
  ' elementu tablicy
  A.nmax = vmax - vmin
  ' Tworzymy dynamicznie
  ' tablicę mapy bitowej
  A.T = New Boolean [A.nmax + 1]
  ' Tworzymy zbiór pusty
  s_clear(A)
End Sub

' Procedura wyznacza
' część wspólną
' zbiorów A i B
'-------------------
Sub s_intersection(ByRef A As Set, _
                   ByRef B As Set, _
                   ByRef C As Set)
  Dim As Integer i

  For i = 0 To A.nmax
    C.T[i] = A.T[i] And B.T[i]
  Next
End Sub

' Procedura kopiuje
' zbiór A do zbioru B
'--------------------
Sub s_copy(ByRef A As Set, _
           ByRef B As Set)
  Dim As Integer i
  
  For i = 0 To A.nmax
    B.T[i] = A.T[i]
  Next
End Sub

' Procedura ustawia
' zbiór pełny
'------------------
Sub s_full(ByRef A As Set)
  Dim As Integer i
  
  For i = 0 To A.nmax
    A.T[i] = True
  Next
End Sub

' Funkcja sprawdza,
' czy zbiór A jest pusty
' true  - tak, jest pusty
' false - nie, nie jest pusty
'----------------------------
Function s_empty(ByRef A As Set) _
                         As Boolean
  Dim As Integer i
  ' Pobieramy bity do m
  Dim As Boolean m = A.T[0]
  
  For i = 1 To A.nmax
    ' Sumujemy logicznie bity
    m = m Or A.T[i]
  Next
  Return Not m
End Function

' Funkcja bada,
' czy element x
' należy do zbioru A
' true  - tak, należy
' false - nie, nie należy
'------------------------
Function s_isin(ByRef A As Set, _
                ByVal x As Integer) _
                        As Boolean
  ' Obliczamy indeks
  Dim As Integer i = x - A.vmin
  Return A.T[i]
End Function

' Procedura wyświetla
' elementy zbioru A
'--------------------
Sub s_print(ByRef A As Set)
  Dim As Integer i

  For i = 0 To A.nmax
    If A.T[i] Then _
      Print Using "& ";i + A.vmin;
  Next
End Sub

' Procedura wyszukuje
' kliki maksymalne
'--------------------
Sub BronKerbosch(ByRef SR As Set, _
                 ByRef SP As Set, _
                 ByRef SX As Set)
  Dim As Set RP, PP, XP
  Dim As SLel Ptr p
  Dim As Integer v

  ' Zwiększamy licznik
  ' wywołań rekurencyjnych
  rcnt += 1
  ' Jeśli zbiory
  ' SP i SX są puste,
  ' to SR zawiera
  ' wierzchołki
  ' kliki maksymalnej
  If s_empty(SP) AndAlso _
     s_empty(SX) Then 
    Print "MAXIMAL CLIQUE: ";
    s_print(SR)
    Print
  Else
    ' Tworzymy puste
    ' zbiory pomocnicze
    s_create(RP, 0, n-1)
    s_create(PP, 0, n-1)
    s_create(XP, 0, n-1)
    ' Przechodzimy przez
    ' kolejne wierzchołki
    ' grafu
    For v = 0 To n - 1
      ' Jeśli wierzchołek v
      ' jest w zbiorze SP, to
      ' go przetwarzamy
      If s_isin(SP, v) Then
        ' Najpierw zerujemy
        ' zbiór sąsiadów
        s_clear(SN)
        ' Przeglądamy listę
        ' sąsiedztwa wierzchołka v
        p = graf[v]
        While p
          ' Każdego sąsiada
          ' umieszczamy w SN
          s_add(SN, p->v)
          p = p->next
        Wend
        ' W RP tworzymy
        ' zbiór SR z dodanym
        ' wierzchołkiem v
        s_copy(SR, RP)
        s_add(RP, v)
        ' W PP tworzymy
        ' iloczyn SP i SN
        s_intersection(SP, SN, PP)
        ' W XP tworzymy
        ' iloczyn SX i SN
        s_intersection(SX, SN, XP)
        ' Wywołanie rekurencyjne
        BronKerbosch(RP, PP, XP)
        ' Z SP usuwamy
        ' wierzchołek v
        s_remove(SP, v)
        ' i dodajemy go do SX
        s_add(SX, v)
      End If
    Next
    ' Usuwamy
    ' zbiory pomocnicze
    Delete [] RP.T
    Delete [] PP.T
    Delete [] XP.T
  End If
End Sub

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

Dim As Set SR, SP, SX
Dim As integer i, u, v
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 puste zbiory
s_create(SR, 0, n-1)
s_create(SP, 0, n-1)
s_create(SX, 0, n-1)
s_create(SN, 0, n-1)
' Tworzymy tablicę
' list sąsiedztwa
graf = New SLel Ptr [n]
For i = 0 To n - 1
  ' Zerujemy listy
  ' sąsiedztwa
  graf[i] = 0
Next
For i = 1 To m
  ' Wierzchołki krawędzi
  Input #1, u, v
  ' Tworzymy
  ' element listy
  p = New SLel
  p->v = u
  ' Element dołączamy
  ' do listy sąsiadów v
  p->next = graf[v]
  graf[v] = p
  ' To samo dla krawędzi
  ' w drugą stronę
  p = New SLel
  p->v = v
  ' Element dołączamy
  ' do listy sąsiadów u
  p->next = graf[u]
  graf[u] = p
Next
Close #1
' W zbiorze SP
' ustawiamy
' wszystkie wierzchołki
s_full(SP)
Print
' Szukamy rekurencyjnie
' klik maksymalnych
BronKerbosch(SR, SP, SX)
Print
Print "RECURSIVE CALLS =";rcnt
Print
' Usuwamy zbiory
Delete [] SR.T
Delete [] SP.T
Delete [] SX.T
Delete [] SN.T
' Usuwamy tablicę
' list sąsiedztwa
For v = 0 To n - 1
  p = graf[v]
  While p
    r = p
    p = p->next
    Delete r
  Wend
Next
Delete [] graf
End
Python (dodatek)
# Algorytm Brona-Kerboscha
# Data: 21.03.2025
# (C)2014 mgr Jerzy Wałaszek
#---------------------------

# Definicja elementu
# listy sąsiedztwa
class SLel:
    def __init__(self):
        # Następny element listy
        self.next = None
        # Wierzchołek docelowy
        self.v = 0


# Definicja struktury zbioru
class Set:
    def __init__(self):
        self.vmin = 0
        self.nmax = 0
        self.t = []


# Obsługa zbiorów
#----------------

# Procedura dodaje
# element x do zbioru A
def s_add(a,x):
    # Obliczamy indeks
    i = x - a.vmin
    # Ustawiamy komórkę
    a.t[i] = True

# Procedura usuwa
# element x ze zbioru
def s_remove(a,x):
    # Obliczamy indeks
    i = x - a.vmin
    # Zerujemy komórkę
    a.t[i] = False

# Procedura usuwa
# wszystkie elementy
# ze zbioru
def s_clear(a):
    # Zerujemy wszystkie komórki
    a.t = [False] * (a.nmax + 1)

# Procedura tworzy zbiór
def s_create(a,vmin,vmax):
    # Wypełniamy strukturę danymi
    a.vmin = vmin
    # Indeks ostatniego
    # elementu tablicy
    a.nmax = vmax - vmin
    # Tworzymy dynamicznie
    # tablicę mapy
    a.t = [False] * (a.nmax + 1)

# Procedura wyznacza
# część wspólną
# zbiorów A i B
def s_intersection(a,b,c):
    for i in range(a.nmax+1):
        c.t[i] = a.t[i] and b.t[i]

# Procedura kopiuje
# zbiór A do zbioru B
def s_copy(a,b):
    b.t = a.t[:]

# Procedura ustawia
# zbiór pełny
def s_full(a):
    a.t = [True] * (a.nmax + 1)

# Funkcja sprawdza,
# czy zbiór A jest pusty
# true  - tak, jest pusty
# false - nie, nie jest pusty
def s_empty(a):
    # sumujemy komórki
    m = a.t[0]
    for i in range(1,a.nmax+1):
        m = m or a.t[i]
    return not m

# Funkcja bada,
# czy element x
# należy do zbioru A
# true  - tak, należy
# false - nie, nie należy
def s_isin(a,x):
    # Obliczamy indeks
    i = x - a.vmin
    return a.t[i]

# Procedura wyświetla
# elementy zbioru A
def s_print(a):
    for i in range(a.nmax+1):
        if a.t[i]:
            print(i + a.vmin,end=" ")

# Procedura wyszukuje
# kliki maksymalne
def bron_kerbosch(sr,sp,sx):
    global n,sn,graf,rcnt
    rp = Set()
    pp = Set()
    xp = Set()
    # Zwiększamy licznik
    # wywołań rekurencyjnych
    rcnt += 1
    # Jeśli zbiory
    # SP i SX są puste,
    # to SR zawiera
    # wierzchołki
    # kliki maksymalnej
    if s_empty(sp) and s_empty(sx):
        print("MAXIMAL CLIQUE: ",end="")
        s_print(sr)
        print()
    else:
        # Tworzymy puste
        # zbiory pomocnicze
        s_create(rp, 0, n-1)
        s_create(pp, 0, n-1)
        s_create(xp, 0, n-1)
        # Przechodzimy przez
        # kolejne wierzchołki
        # grafu
        for v in range(n):
            # Jeśli wierzchołek v
            # jest w zbiorze SP, to
            # go przetwarzamy
            if s_isin(sp,v):
                # Najpierw zerujemy
                # zbiór sąsiadów
                s_clear(sn)
                # Przeglądamy listę
                # sąsiedztwa
                # wierzchołka v
                p = graf[v]
                while p:
                    # Każdego sąsiada
                    # umieszczamy w SN
                    s_add(sn, p.v)
                    p = p.next
                # W RP tworzymy
                # zbiór SR z dodanym
                # wierzchołkiem v
                s_copy(sr, rp)
                s_add(rp, v)
                # W PP tworzymy
                # iloczyn SP i SN
                s_intersection(sp,sn,pp)
                # W XP tworzymy
                # iloczyn SX i SN
                s_intersection(sx,sn,xp)
                # Wywołanie rekurencyjne
                bron_kerbosch(rp,pp,xp)
                # Z SP usuwamy
                # wierzchołek v
                s_remove(sp, v)
                # i dodajemy go do SX
                s_add(sx, v)
        # Usuwamy
        # zbiory pomocnicze
        del rp,pp,xp

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

sr = Set()
sp = Set()
sx = Set()
sn = Set()
# Odczytujemy liczbę
# wierzchołków i krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy puste zbiory
s_create(sr, 0, n-1)
s_create(sp, 0, n-1)
s_create(sx, 0, n-1)
s_create(sn, 0, n-1)
# Tworzymy tablicę
# list sąsiedztwa
graf = [None] * n
for i in range(m):
    # Wierzchołki krawędzi
    x = input().split()
    u = int(x[0])
    v = int(x[1])
    # Tworzymy
    # element listy
    p = SLel()
    p.v = u
    # Element dołączamy
    # do listy sąsiadów v
    p.next = graf[v]
    graf[v] = p
    # To samo dla krawędzi
    # w drugą stronę
    p = SLel()
    p.v = v
    # Element dołączamy
    # do listy sąsiadów u
    p.next = graf[u]
    graf[u] = p
# W zbiorze SP
# ustawiamy
# wszystkie wierzchołki
s_full(sp)
print()
# Szukamy rekurencyjnie
# klik maksymalnych\
rcnt = 0
bron_kerbosch(sr, sp, sx)
print()
print("RECURSIVE CALLS =",rcnt)
print()
# Usuwamy zbiory
del sr,sp,sx,sn
# Usuwamy tablicę
# list sąsiedztwa
for v in range(n):
    while graf[v]:
        graf[v] = graf[v].next
del graf
Wynik:
8 19
0 1
0 2
0 5
0 6
1 2
1 3
1 5
1 6
2 4
2 5
2 6
2 7
3 4
3 6
3 7
4 6
5 6
5 7
6 7

MAXIMAL CLIQUE: 0 1 2 5 6
MAXIMAL CLIQUE: 1 3 6
MAXIMAL CLIQUE: 2 4 6
MAXIMAL CLIQUE: 2 5 6 7
MAXIMAL CLIQUE: 3 4 6
MAXIMAL CLIQUE: 3 6 7

RECURSIVE CALLS = 52
 obrazek

do podrozdziału  do strony 

Rozwiązanie nr 2

Algorytm Brona-Kerboscha da się ulepszyć, tak aby wykonywał mniej wywołań rekurencyjnych. W tym celu przed wejściem do pętli rozwijającej zbiór P tworzymy sumę zbiorów  P i X, a następnie znajdujemy w niej taki wierzchołek u, który posiada najwięcej sąsiadów w zbiorze P. Teraz zamiast iterować przez wierzchołki w P iterujemy przez kolejne wierzchołki różnicy zbiorów P i N(u), gdzie N(u) oznacza zbiór sąsiadów wierzchołka u, który nazywamy piwotem. Zbiór P\N(u) zawiera mniej wierzchołków, zatem liczba wywołań rekurencyjnych spadnie.

Algorytm Brona-Kerboscha znajdowania wszystkich klik maksymalnych w grafie nieskierowanym - wersja nr 2

BronKerbosch(n,graf,R,P,X)

Wejście:

n : liczba wierzchołków w grafie; n ∈ C.
graf : zadany w dowolnie wybrany sposób, algorytm tego nie precyzuje.
R : zbiór wierzchołków, które są częściowym wynikiem znajdowania kliki.
P : zbiór wierzchołków, które są kandydatami do rozważenia.
: zbiór wierzchołków pominiętych.

Wyjście:

Wszystkie kliki maksymalne w grafie

Elementy pomocnicze:

u,v,w : numery wierzchołków grafu; u,v,w ∈ C.
P',P" R' X',N : pomocnicze zbiory wierzchołków.
ncmax,nc : liczniki wierzchołków; ncmax,nc ∈ C.

Lista kroków:

K01: Jeśli (P jest pusty) obrazek (X jest pusty),
     to pisz R ; znaleziona klika maksymalna
     i zakończ
K02: P" ← suma zbiorów P i X ; będziemy szukać piwota
     ; w sumie zbiorów P i X
K03: ncmax ← 0 ; zerujemy globalny licznik sąsiadów
K04: Dla każdego wierzchołka u w P" :
     wykonuj kroki K05…K07 ; przeglądamy graf,
     ; biorąc pod uwagę tylko wierzchołki w P
K05:   nc ← 0 ; zerujemy bieżący licznik sąsiadów
K06:   Dla każdego sąsiada w wierzchołka u:
       wykonuj:
       Jeśli w należy do P, ; jeśli sąsiad jest w P,
       to ncnc + 1 ; to zwiększamy bieżący licznik sąsiadów
K07:   Jeśli ncncmax, ; jeśli wierzchołek u ma nie mniej
       ; sąsiadów niż jest w ncmax, to
       to: vu ; zapamiętujemy ten wierzchołek
       i ncmaxnc ; i zapamiętujemy liczbę jego sąsiadów
K08: P" ← P
K09: Dla każdego sąsiada u wierzchołka v:
     wykonujP" ← P"\u ; tworzymy zbiór P bez sąsiadów piwota
K10: Dla każdego wierzchołka v w P":
     wykonuj kroki K11…K18
K11:   Zeruj zbiór N
K12:   Dla każdego sąsiada u wierzchołka v:
       wykonuj: NN+u ; tworzymy zbiór sąsiadów
K13:   R' ← R+v ; w R' tworzymy zbiór R z dodanym wierzchołkiem v
K14:   P' ← iloczyn zbiorów P i N ; z P' usuwamy wierzchołki,
       ; które nie są sąsiadami v
K15:   X' ← iloczyn zbiorów X i N ; z X' usuwamy wierzchołki,
       ; które nie są sąsiadami v
K16:   BronKerbosch(n,graf,R',P',X') ; wywołanie rekurencyjne
K17:   PP \ v ; ze zbioru P usuwamy wierzchołek v
K18:   RR + v ; do zbioru R dodajemy wierzchołek v
K19: Zakończ 

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 wyszukuje w nim wszystkie kliki maksymalne za pomocą algorytmu Brona-Kerboscha. Zbiory zostały zaimplementowane w tablicy logicznej.
Program dodatkowo wypisuje liczbę wywołań rekurencyjnych algorytmu Brona-Kerboscha dla celów porównawczych.
Dane przykładowe (przekopiuj do schowka i wklej do okna konsoli):
obrazek 8 19
0 1
0 2
0 5
0 6
1 2
1 3
1 5
1 6
2 4
2 5
2 6
2 7
3 4
3 6
3 7
4 6
5 6
5 7
6 7
Pascal
// Algorytm Brona-Kerboscha
// z piwotem
// Data: 15.07.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------

program cliques;

// Definicja elementu
// listy sąsiedztwa
type
  PSLel = ^SLel;
  SLel = record
    // Następny element listy
    next : PSLel;
    // Wierzchołek docelowy
    v : integer;
  end;

// Definicja struktury zbioru
  SSet =  record
    vmin  : integer;
    nmax  : integer;
    T     : array of boolean;
  end;

// Zmienne globalne
var
  // Liczba wierzchołków
  // i krawędzi grafu
  n, m  : integer;
  // Tablica list sąsiedztwa
  graf : array of PSLel;
  // Zbiór sąsiadów
  SN   : SSet;
  // Licznik wywołań
  // rekurencyjnych
  rcnt : integer;

// Obsługa zbiorów
//----------------
// Procedura dodaje
// element x do zbioru A
//----------------------
procedure s_add(var A : SSet;
                    x : integer);
var
  i : integer;
begin
  // Obliczamy indeks
  i := x - A.vmin;
  // Ustawiamy element
  A.T[i] := true;
end;

// Procedura usuwa
// element x ze zbioru
//--------------------
procedure s_remove(var A : SSet;
                       x : integer);
var
  i : integer;
begin
  // Obliczamy indeks
  i := x - A.vmin;
  // Zerujemy element
  A.T[i] := false;
end;

// Procedura usuwa
// wszystkie elementy
// ze zbioru
//-------------------
procedure s_clear(var A : SSet);
var
  i : integer;
begin
  // Zerujemy tablicę
  for i := 0 to A.nmax do
    A.T[i] := false;
end;

// Procedura tworzy zbiór
//-----------------------
procedure s_create(var A : SSet;
              vmin, vmax : integer);
begin
  // Wypełniamy
  // strukturę danymi
  A.vmin := vmin;
  // Indeks ostatniego
  // elementu tablicy
  A.nmax := vmax - vmin;
  // Tworzymy dynamicznie
  // tablicę logiczną
  SetLength(A.T, A.nmax + 1);
  // Tworzymy zbiór pusty
  s_clear(A);
end;

// Procedura wyznacza
// sumę zbiorów A i B
//-------------------
procedure s_union
          (var A, B, C : SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    C.T[i] := A.T[i] or B.T[i];
end;

// Procedura wyznacza
// część wspólną zbiorów A i B
//----------------------------
procedure s_intersection
          (var A, B, C :SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    C.T[i] := A.T[i] and B.T[i];
end;

// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
procedure s_copy(var A, B : SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    B.T[i] := A.T[i];
end;

// Procedura ustawia
// zbiór pełny
procedure s_full(var A : SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    A.T[i] := true;
end;

// Funkcja sprawdza,
// czy zbiór A jest pusty
// true  - jest
// false - nie jest
//-----------------------
function s_empty(var A : SSet)
                       : boolean;
var
  i : integer;
  m : boolean;
begin
  // Pobieramy bity do m
  m := A.T[0];
  for i := 1 to A.nmax do
    // Sumujemy logicznie bity
    m := m or A.T[i];
  s_empty := (m = false);
end;

// Funkcja bada,
// czy element x
// należy do zbioru A
// true  - tak, należy
// false - nie, nie należy
//------------------------
function s_isin(var A : SSet;
                    x : integer)
                      : boolean;
var
  i : integer;
begin
  // Obliczamy indeks
  i := x - A.vmin;
  s_isin := A.T[i]
end;

// Procedura wyświetla
// elementy zbioru A
//--------------------
procedure s_print(var A : SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    if A.T[i] then
        write(i + A.vmin,' ');
end;

// Procedura wyszukuje
// kliki maksymalne
//--------------------
procedure BronKerbosch
          (var SR, SP, SX : SSet);
var
  RP, PP, XP, PPP : SSet;
  p : PSLel;
  v, u, ncmax, nc : integer;
begin
  // Zwiększamy licznik
  // wywołań rekurencyjnych
  inc(rcnt);
   // Jeśli zbiory SP i SX
   // są puste, to SR zawiera
   // wierzchołki kliki
   // maksymalnej
  if s_empty(SP) and
     s_empty(SX) then
  begin
    write ('MAXIMAL CLIQUE: ');
    s_print(SR);
    writeln;
  end
  else
  begin
    // Tworzymy zbiór
    // pomocniczy PPP
    s_create(PPP, 0, n-1);
    // W PPP tworzymy sumę
    // zbiorów SP i SX
    s_union(SP, SX, PPP);
    // Zerujemy licznik
    // sąsiadów
    ncmax := 0;
    // Przechodzimy przez
    // kolejne wierzchołki
    // grafu
    for u := 0 to n-1 do
      // Jeśli wierzchołek
      // jest w PPP, to
      // przetwarzamy go
      if s_isin(PPP, u) then
      begin
        // Zerujemy bieżący
        // licznik sąsiadów
        nc := 0;
        // Badamy sąsiadów
        // wierzchołka v
        p := graf[u];
        while p <> nil do
        begin
          // Jeśli sąsiad w SP,
          // zwiększamy nc
          if s_isin(SP, p^.v) then
            inc(nc);
          // Następny sąsiad
          p := p^.next;
        end;
        // Jeśli liczba sąsiadów w P
        // jest większa od ncmax,
        if nc >= ncmax then
        begin
          // to zapamiętujemy
          // wierzchołek
          v := u;
          // oraz jego liczbę
          // sąsiadów w P
          ncmax := nc;
        end;
      end;
    // W PPP umieszczamy zbiór SP
    s_copy(SP, PPP);
    p := graf[v];
    // Przeglądamy listę
    // sąsiadów piwota
    while p <> nil do
    begin
      // Z PPP usuwamy każdego
      // sąsiada piwota
      s_remove(PPP, p^.v);
      // Następny sąsiad
      p := p^.next;
    end;
    // Tworzymy puste
    // zbiory pomocnicze
    s_create(RP, 0, n-1);
    s_create(PP, 0, n-1);
    s_create(XP, 0, n-1);
    // Przechodzimy przez
    // kolejne wierzchołki grafu
    for v := 0 to n-1 do
      // Jeśli wierzchołek v
      // jest w zbiorze PPP,
      // to go przetwarzamy
      if s_isin(PPP, v) then
      begin
        // Najpierw zerujemy
        // zbiór sąsiadów
        s_clear(SN);
        // Przeglądamy listę
        // sąsiedztwa wierzchołka v
        p := graf[v];
        while p <> nil do
        begin
          // Każdego sąsiada
          // umieszczamy w SN
          s_add(SN, p^.v);
          p := p^.next;
        end;
        // W RP tworzymy zbiór SR
        // z dodanym wierzchołkiem v
        s_copy(SR, RP);
        s_add(RP, v);
        // W PP tworzymy
        // iloczyn SP i SN
        s_intersection(SP, SN, PP);
        // W XP tworzymy
        // iloczyn SX i SN
        s_intersection(SX, SN, XP);
        // Wywołanie rekurencyjne
        BronKerbosch(RP, PP, XP);
        // Z SP usuwamy
        // wierzchołek v
        s_remove(SP, v);
        // i dodajemy go do SX
        s_add(SX, v);
      end;
    // Usuwamy zbiory pomocnicze
    SetLength(RP.T, 0);
    SetLength(PP.T, 0);
    SetLength(XP.T, 0);
    SetLength(PPP.T, 0);
  end;
end;

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

var
  SR, SP, SX : SSet;
  i, u, v : integer;
  p, r : PSLel;
begin
  // Odczytujemy liczbę
  // wierzchołków i krawędzi
  read(n, m);
  // Tworzymy puste zbiory
  s_create(SR, 0, n-1);
  s_create(SP, 0, n-1);
  s_create(SX, 0, n-1);
  s_create(SN, 0, n-1);
  // Tworzymy tablicę
  // list sąsiedztwa
  SetLength(graf, n);
  // Zerujemy listy sąsiedztwa
  for i := 0 to n-1 do
    graf[i] := nil;
  for i := 1 to m do
  begin
    // Wierzchołki krawędzi
    read(u, v);
    // Tworzymy element listy
    new(p);
    p^.v := u;
    // Element dołączamy
    // do listy sąsiadów v
    p^.next := graf[v];
    graf[v] := p;
    // To samo dla krawędzi
    // w drugą stronę
    new(p);
    p^.v := v;
    // Element dołączamy
    // do listy sąsiadów u
    p^.next := graf[u];
    graf[u] := p;
  end;
  // W zbiorze SP ustawiamy
  // wszystkie wierzchołki
  s_full(SP);
  writeln;
  // Zerujemy licznik
  // wywołań rekurencyjnych
  rcnt := 0;
  // Szukamy rekurencyjnie
  // klik maksymalnych
  BronKerbosch(SR, SP, SX);
  writeln;
  writeln('RECURSIVE CALLS = ',
          rcnt);
  writeln;
  // Usuwamy zbiory
  SetLength(SR.T, 0);
  SetLength(SP.T, 0);
  SetLength(SX.T, 0);
  SetLength(SN.T, 0);
  // Usuwamy tablicę
  // list sąsiedztwa
  for v := 0 to n-1 do
  begin
    p := graf[v];
    while p <> nil do
    begin
      r := p;
      p := p^.next;
      dispose(r);
    end;
  end;
  SetLength(graf, 0);
end.
C++
// Algorytm Brona-Kerboscha
// z piwotem
// Data: 15.07.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------

#include <iostream>
#include <iomanip>

using namespace std;

// Definicja elementu
// listy sąsiedztwa
struct SLel
{
  // Następny element listy
  SLel *next;
  // Wierzchołek docelowy
  int v;
};

// Definicja struktury zbioru
struct Set
{
  int vmin, nmax;
  bool *T;
};

// Zmienne globalne
//-----------------
// Liczba wierzchołków
// i krawędzi grafu
int n, m;
// Tablica list sąsiedztwa
SLel ** graf;
// Zbiór sąsiadów
Set SN;
// Licznik wywołań
// rekurencyjnych
int rcnt = 0;

// Obsługa zbiorów
//----------------

// Procedura dodaje
// element x do zbioru A
//----------------------
void s_add(Set & A, int x)
{
  // Obliczamy indeks
  int i = x - A.vmin;
  // Ustawiamy bit na 1
  A.T[i] = true;
}

// Procedura usuwa
// element x ze zbioru
//--------------------
void s_remove(Set & A, int x)
{
  // Obliczamy indeks
  int i = x - A.vmin;
  // Ustawiamy bit na 0
  A.T[i] = false;
}

// Procedura usuwa wszystkie
// elementy ze zbioru
//--------------------------
void s_clear(Set & A)
{
  // Zerujemy tablicę
  for(int i = 0; i <= A.nmax; i++)
    A.T[i] = false;
}

// Procedura tworzy zbiór
//-----------------------
void s_create(Set & A,
              int vmin,
              int vmax)
{
  // Wypełniamy strukturę danymi
  A.vmin = vmin;
  // Indeks ostatniego
  // elementu tablicy
  A.nmax = vmax - vmin;
  // Tworzymy dynamicznie
  // tablicę logiczną
  A.T = new bool [A.nmax+1];
  // Tworzymy zbiór pusty
  s_clear(A);
}

// Procedura wyznacza
// sumę zbiorów A i B
//-------------------
void s_union(Set & A,
             Set & B,
             Set & C)
{
  for(int i = 0; i <= A.nmax; i++)
    C.T[i] = A.T[i] || B.T[i];
}

// Procedura wyznacza część
// wspólną zbiorów A i B
//-------------------------
void s_intersection(Set & A,
                    Set & B,
                    Set & C)
{
  for(int i = 0; i <= A.nmax; i++)
    C.T[i] = A.T[i] && B.T[i];
}

// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
void s_copy(Set & A, Set & B)
{
  for(int i = 0; i <= A.nmax; i++)
    B.T[i] = A.T[i];
}

// Procedura ustawia
// zbiór pełny
//-------------------
void s_full(Set & A)
{
  for(int i = 0; i <= A.nmax; i++)
    A.T[i] = true;
}

// Funkcja sprawdza,
// czy zbiór A jest pusty
// true  - tak, jest pusty
// false - nie, nie jest pusty
//----------------------------
bool s_empty(Set A)
{
  // Pobieramy bity do m
  bool m = A.T[0];
  // Sumujemy logicznie bity
  for(int i = 1; i <= A.nmax; i++)
    m = m || A.T[i];
  return !m;
}

// Funkcja bada, czy element
// x należy do zbioru A
// true  - tak, należy
// false - nie, nie należy
//--------------------------
bool s_isin(Set & A, int x)
{
  // Obliczamy indeks
  int i = x - A.vmin;
  return A.T[i];
}

// Procedura wyświetla
// elementy zbioru A
//--------------------
void s_print(Set A)
{
  int i;

  for(i = 0; i <= A.nmax; i++)
    if(A.T[i])
      cout << i + A.vmin << " ";
}

// Procedura wyszukuje
// kliki maksymalne
//--------------------
void BronKerbosch(Set & SR,
                  Set & SP,
                  Set & SX)
{
  Set RP, PP, XP, PPP;
  SLel * p;
  int v, u, ncmax, nc;

  // Zwiększamy licznik
  // wywołań rekurencyjnych
  rcnt++;
  // Jeśli zbiory SP i SX są puste,
  // to SR zawiera wierzchołki
  // kliki maksymalnej
  if(s_empty(SP) &&
     s_empty (SX))
  {
    cout << "MAXIMAL CLIQUE: ";
    s_print(SR);
    cout << endl;
  }
  else
  {
    // Tworzymy zbiór
    // pomocniczy PPP
    s_create(PPP, 0, n-1);
    // W PPP tworzymy sumę
    // zbiorów SP i SX
    s_union(SP, SX, PPP);
    // Zerujemy licznik sąsiadów
    ncmax = 0;
    // Przechodzimy przez
    // kolejne wierzchołki grafu
    for(u = 0; u < n; u++)
      // Jeśli wierzchołek
      // jest w PPP, to
      // przetwarzamy go
      if(s_isin(PPP, u))
      {
        // Zerujemy bieżący
        // licznik sąsiadów
        nc = 0;
        // Badamy sąsiadów
        // wierzchołka v
        for(p = graf[u];
            p; p = p->next)
          // Jeśli sąsiad w SP,
          // zwiększamy nc
          if(s_isin(SP, p->v))
            nc++;
        // Jeśli liczba sąsiadów
        // w P jest większa
        // od ncmax,
        if(nc >= ncmax)
        {
          // to zapamiętujemy
          // wierzchołek
          v = u;
          // oraz jego liczbę
          // sąsiadów w P
          ncmax = nc;
        }
      }
    // W PPP umieszczamy zbiór SP
    s_copy(SP, PPP);
    // Przeglądamy listę
    // sąsiadów piwota
    for(p = graf[v]; p; p = p->next)
      // Z PPP usuwamy każdego
      // sąsiada piwota
      s_remove(PPP, p->v);
    // Tworzymy puste
    // zbiory pomocnicze
    s_create(RP, 0, n-1);
    s_create(PP, 0, n-1);
    s_create(XP, 0, n-1);
    // Przechodzimy przez kolejne
    // wierzchołki grafu
    for(v = 0; v < n; v++)
      // Jeśli wierzchołek v
      // jest w zbiorze PPP, to
      // go przetwarzamy
      if(s_isin(PPP, v))
      {
        // Najpierw zerujemy
        // zbiór sąsiadów
        s_clear(SN);
        // Przeglądamy listę
        // sąsiedztwa wierzchołka v
        for(p = graf[v];
            p; p = p->next)
          // Każdego sąsiada
          // umieszczamy w SN
          s_add(SN, p->v);
        // W RP tworzymy zbiór SR
        // z dodanym wierzchołkiem v
        s_copy(SR, RP);
        s_add(RP, v);
        // W PP tworzymy
        // iloczyn SP i SN
        s_intersection(SP, SN, PP);
        // W XP tworzymy
        // iloczyn SX i SN
        s_intersection(SX, SN, XP);
        // Wywołanie rekurencyjne
        BronKerbosch(RP, PP, XP);
        // Z SP usuwamy
        // wierzchołek v
        s_remove(SP, v);
        // i dodajemy go do SX
        s_add(SX, v);
      }
    // Usuwamy zbiory pomocnicze
    delete[] RP.T;
    delete[] PP.T;
    delete[] XP.T;
    delete[] PPP.T;
  }
}

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

int main()
{
  Set SR, SP, SX;
  int i, u, v;
  SLel *p, *r;

  // Odczytujemy liczbę
  // wierzchołków i krawędzi
  cin >> n >> m;
  // Tworzymy puste zbiory
  s_create(SR, 0, n-1);
  s_create(SP, 0, n-1);
  s_create(SX, 0, n-1);
  s_create(SN, 0, n-1);
  // Tworzymy tablicę
  // list sąsiedztwa
  graf = new SLel * [n];
  // Zerujemy listy sąsiedztwa
  for(i = 0; i < n; i++)
    graf[i] = nullptr;
  for(i = 0; i < m; i++)
  {
    // Wierzchołki krawędzi
    cin >> u >> v;
    // Tworzymy element listy
    p = new SLel;
    p->v = u;
    // Element dołączamy
    // do listy sąsiadów v
    p->next = graf[v];
    graf[v] = p;
    // To samo dla krawędzi
    // w drugą stronę
    p = new SLel;
    p->v = v;
    // Element dołączamy
    // do listy sąsiadów u
    p->next = graf[u];
    graf[u] = p;
  }
  // W zbiorze SP ustawiamy
  // wszystkie wierzchołki
  s_full(SP);
  cout << endl;
  // Szukamy rekurencyjnie
  // klik maksymalnych
  BronKerbosch(SR, SP, SX);
  cout << endl
       << "RECURSIVE CALLS = "
       << rcnt << endl << endl;
  // Usuwamy zbiory
  delete [] SR.T;
  delete [] SP.T;
  delete [] SX.T;
  delete [] SN.T;
  // Usuwamy tablicę
  // list sąsiedztwa
  for(v = 0; v < n; v++)
  {
    p = graf[v];
    while(p)
    {
      r = p;
      p = p->next;
      delete r;
    }
  }
  delete [] graf;
  return 0;
}
Basic
' Algorytm Brona-Kerboscha
' z piwotem
' Data: 15.07.2014
' (C)2014 mgr Jerzy Wałaszek
'---------------------------

' Definicja elementu
' listy sąsiedztwa
Type SLel
  ' Następny element listy
  next As SLel Ptr
  ' Wierzchołek docelowy
  v As Integer
End Type

' Definicja struktury zbioru
Type Set
  vmin As Integer
  nmax As Integer
  T As Boolean Ptr
End Type

' Zmienne globalne
'-----------------
' Liczba wierzchołków
' i krawędzi grafu
Dim Shared As Integer n, m
' Tablica list sąsiedztwa
Dim Shared As SLel Ptr Ptr graf
' Zbiór sąsiadów
Dim Shared As Set SN
' Licznik wywołań rekurencyjnych
Dim Shared As Integer rcnt = 0

' Obsługa zbiorów
'----------------
' Procedura dodaje
' element x do zbioru A
'----------------------
Sub s_add(ByRef A As Set, _
          ByVal x As Integer)
  Dim As Integer i

  ' Obliczamy indeks
  i = x - A.vmin
  ' Ustawiamy bit na 1
  A.T[i] = True
End Sub

' Procedura usuwa
' element x ze zbioru
'--------------------
Sub s_remove(ByRef A As Set, _
             ByVal x As Integer)
  Dim As Integer i

  ' Obliczamy indeks
  i = x - A.vmin
  ' Ustawiamy bit na 0
  A.T[i] = False
End Sub

' Procedura usuwa
' wszystkie elementy
' ze zbioru
'-------------------
Sub s_clear(ByRef A As Set)
  Dim As Integer i
  
  ' Zerujemy tablicę
  For i = 0 To A.nmax
    A.T[i] = False
  Next
End Sub

' Procedura tworzy zbiór
'-----------------------
Sub s_create(ByRef A As Set, _
             ByVal vmin As Integer, _
             ByVal vmax As Integer)
  ' Wypełniamy strukturę danymi
  A.vmin = vmin
  ' Indeks ostatniego
  ' elementu tablicy
  A.nmax = vmax - vmin
  ' Tworzymy dynamicznie
  ' tablicę mapy bitowej
  A.T = New Boolean [A.nmax + 1]
  ' Tworzymy zbiór pusty
  s_clear(A)
End Sub

' Procedura wyznacza
' sumę zbiorów A i B
'-------------------
Sub s_union(ByRef A As Set, _
            ByRef B As Set, _
            ByRef C As Set)
  Dim As Integer i

  For i = 0 To A.nmax
  	 C.T[i] = A.T[i] Or B.T[i]
  Next
End Sub

' Procedura wyznacza
' część wspólną
' zbiorów A i B
'-------------------
Sub s_intersection(ByRef A As Set, _
                   ByRef B As Set, _
                   ByRef C As Set)
  Dim As Integer i

  For i = 0 To A.nmax
    C.T[i] = A.T[i] And B.T[i]
  Next
End Sub

' Procedura kopiuje
' zbiór A do zbioru B
'--------------------
Sub s_copy(ByRef A As Set, _
           ByRef B As Set)
  Dim As Integer i
  
  For i = 0 To A.nmax
    B.T[i] = A.T[i]
  Next
End Sub

' Procedura ustawia
' zbiór pełny
'------------------
Sub s_full(ByRef A As Set)
  Dim As Integer i
  
  For i = 0 To A.nmax
    A.T[i] = True
  Next
End Sub

' Funkcja sprawdza,
' czy zbiór A jest pusty
' true  - tak, jest pusty
' false - nie, nie jest pusty
'----------------------------
Function s_empty(ByRef A As Set) _
                         As Boolean
  Dim As Integer i
  ' Pobieramy bity do m
  Dim As Boolean m = A.T[0]
  
  For i = 1 To A.nmax
    ' Sumujemy logicznie bity
    m = m Or A.T[i]
  Next
  Return Not m
End Function

' Funkcja bada,
' czy element x
' należy do zbioru A
' true  - tak, należy
' false - nie, nie należy
'------------------------
Function s_isin(ByRef A As Set, _
                ByVal x As Integer) _
                        As Boolean
  ' Obliczamy indeks
  Dim As Integer i = x - A.vmin
  Return A.T[i]
End Function

' Procedura wyświetla
' elementy zbioru A
'--------------------
Sub s_print(ByRef A As Set)
  Dim As Integer i

  For i = 0 To A.nmax
    If A.T[i] Then _
      Print Using "& ";i + A.vmin;
  Next
End Sub

' Procedura wyszukuje
' kliki maksymalne
'--------------------
Sub BronKerbosch(ByRef SR As Set, _
                 ByRef SP As Set, _
                 ByRef SX As Set)
  Dim As Set RP, PP, XP, PPP
  Dim As SLel Ptr p
  Dim As Integer v, u, ncmax, nc

  ' Zwiększamy licznik
  ' wywołań rekurencyjnych
  rcnt += 1
  ' Jeśli zbiory SP i SX są puste,
  ' to SR zawiera wierzchołki
  ' kliki maksymalnej
  If s_empty(SP) AndAlso _
     s_empty(SX) Then
    Print "MAXIMAL CLIQUE: ";
    s_print(SR)
    Print
  Else
    ' Tworzymy zbiór
    ' pomocniczy PPP
    s_create(PPP, 0, n-1)
    ' W PPP tworzymy sumę
    ' zbiorów SP i SX
    s_union(SP, SX, PPP)
    ' Zerujemy
    ' licznik sąsiadów
    ncmax = 0
    ' Przechodzimy przez
    ' kolejne wierzchołki grafu
    For u = 0 To n-1
      ' Jeśli wierzchołek
      ' jest w PPP,
      ' to przetwarzamy go
      If s_isin(PPP, u) Then
        ' Zerujemy bieżący
        ' licznik sąsiadów
        nc = 0
        p = graf[u]
        ' Badamy sąsiadów
        ' wierzchołka v
        While p <> 0
          If s_isin(SP, p->v) Then
            ' Jeśli sąsiad w SP,
            ' zwiększamy nc
            nc += 1
          End If
          ' Następny sąsiad
          p = p->next
        Wend
        ' Jeśli liczba sąsiadów
        ' w P jest większa od ncmax,
        If nc >= ncmax Then 
          ' to zapamiętujemy
          ' wierzchołek
          v = u
          ' oraz jego liczbę
          ' sąsiadów w P
          ncmax = nc
        End If
      End If
    Next
    ' W PPP umieszczamy zbiór SP
    s_copy(SP, PPP)
    p = graf[v]
    ' Przeglądamy listę
    ' sąsiadów piwota
    While p <> 0
      ' Z PPP usuwamy każdego
      ' sąsiada piwota
      s_remove(PPP, p->v)
      ' Następny sąsiad
      p = p->next
    Wend
    ' Tworzymy puste
    ' zbiory pomocnicze
    s_create(RP, 0, n-1)
    s_create(PP, 0, n-1)
    s_create(XP, 0, n-1)
    ' Przechodzimy przez
    ' kolejne wierzchołki grafu
    For v = 0 To n-1
      ' Jeśli wierzchołek v
      ' jest w zbiorze PPP, to
      ' go przetwarzamy
      If s_isin(PPP, v) Then
        ' Najpierw zerujemy
        ' zbiór sąsiadów
        s_clear(SN)
        ' Przeglądamy listę
        ' sąsiedztwa wierzchołka v
        p = graf[v]
        While p <> 0
          ' Każdego sąsiada
          ' umieszczamy w SN
          s_add(SN, p->v)
          p = p->next
        Wend
        ' W RP tworzymy
        ' zbiór SR z dodanym
        ' wierzchołkiem v
        s_copy(SR, RP)
        s_add(RP, v)
        ' W PP tworzymy
        ' iloczyn SP i SN
        s_intersection(SP, SN, PP)
        ' W XP tworzymy
        ' iloczyn SX i SN
        s_intersection(SX, SN, XP)
        ' Wywołanie rekurencyjne
        BronKerbosch(RP, PP, XP)
        ' Z SP usuwamy wierzchołek v
        s_remove(SP, v)
        ' i dodajemy go do SX
        s_add(SX, v)
      End If
    Next
    ' Usuwamy zbiory pomocnicze
    Delete [] RP.T
    Delete [] PP.T
    Delete [] XP.T
    Delete [] PPP.T
  End If
End Sub

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

Dim As Set SR, SP, SX
Dim As integer i, u, v
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 puste zbiory
s_create(SR, 0, n-1)
s_create(SP, 0, n-1)
s_create(SX, 0, n-1)
s_create(SN, 0, n-1)
' Tworzymy tablicę
' list sąsiedztwa
graf = New SLel Ptr [n]
For i = 0 To n-1
  ' Zerujemy listy sąsiedztwa
  graf[i] = 0
Next
For i = 1 To m
  ' Wierzchołki krawędzi
  Input #1, u, v
  ' Tworzymy element listy
  p = New SLel
  p->v = u
  ' Element dołączamy
  ' do listy sąsiadów v
  p->next = graf[v]
  graf[v] = p
  ' To samo dla krawędzi
  ' w drugą stronę
  p = New SLel
  p->v = v
  ' Element dołączamy
  ' do listy sąsiadów u
  p->next = graf[u]
  graf[u] = p
Next
Close #1
' W zbiorze SP ustawiamy
' wszystkie wierzchołki
s_full(SP)
Print
' Szukamy rekurencyjnie
' klik maksymalnych
BronKerbosch(SR, SP, SX)
Print
Print "RECURSIVE CALLS =";rcnt
Print
' Usuwamy zbiory
Delete [] SR.T
Delete [] SP.T
Delete [] SX.T
Delete [] SN.T
' Usuwamy tablicę
' list sąsiedztwa
For v = 0 To n-1
  p = graf[v]
  While p <> 0
    r = p
    p = p->next
    Delete r
  Wend
Next
Delete [] graf
End
Python (dodatek)
# Algorytm Brona-Kerboscha
# z piwotem
# Data: 26.03.2025
# (C)2025 mgr Jerzy Wałaszek
#---------------------------

# Definicja elementu
# listy sąsiedztwa
class SLel:
    def __init__(self):
        # Następny element listy
        self.next = None
        # Wierzchołek docelowy
        self.v = 0


# Definicja struktury zbioru
class Set:
    def __init__(self):
        self.vmin = 0
        self.nmax = 0
        self.t = []

# Obsługa zbiorów
#----------------

# Procedura dodaje
# element x do zbioru A
def s_add(a,x):
    # Obliczamy indeks
    i = x - a.vmin
    # Ustawiamy komórkę
    a.t[i] = True

## Procedura usuwa
# element x ze zbioru
def s_remove(a,x):
    # Obliczamy indeks
    i = x - a.vmin
    # Zerujemy komórkę
    a.t[i] = False

# Procedura usuwa
# wszystkie elementy
# ze zbioru
def s_clear(a):
    # Zerujemy wszystkie komórki
    a.t = [False] * (a.nmax + 1)

# Procedura tworzy zbiór
def s_create(a,vmin,vmax):
    # Wypełniamy strukturę danymi
    a.vmin = vmin
    # Indeks ostatniego
    # elementu tablicy
    a.nmax = vmax - vmin
    # Tworzymy dynamicznie
    # tablicę mapy
    a.t = [False] * (a.nmax + 1)

# Procedura wyznacza
# sumę zbiorów A i B
def s_union(a,b,c):
    for i in range(a.nmax+1):
        c.t[i] = a.t[i] or b.t[i]

# Procedura wyznacza
# część wspólną
# zbiorów A i B
def s_intersection(a,b,c):
    for i in range(a.nmax+1):
        c.t[i] = a.t[i] and b.t[i]

# Procedura kopiuje
# zbiór A do zbioru B
def s_copy(a,b):
    b.t = a.t[:]

# Procedura ustawia
# zbiór pełny
def s_full(a):
    a.t = [True] * (a.nmax + 1)

# Funkcja sprawdza,
# czy zbiór A jest pusty
# true  - tak, jest pusty
# false - nie, nie jest pusty
def s_empty(a):
    # sumujemy komórki
    m = a.t[0]
    for i in range(1,a.nmax+1):
        m = m or a.t[i]
    return not m

# Funkcja bada,
# czy element x
# należy do zbioru A
# true  - tak, należy
# false - nie, nie należy
def s_isin(a,x):
    # Obliczamy indeks
    i = x - a.vmin
    return a.t[i]

# Procedura wyświetla
# elementy zbioru A
def s_print(a):
    for i in range(a.nmax+1):
        if a.t[i]:
            print(i + a.vmin,end=" ")

# Procedura wyszukuje
# kliki maksymalne
def bron_kerbosch(sr,sp,sx):
    global rcnt,n,graf
    rp = Set()
    pp = Set()
    xp = Set()
    ppp = Set()
    # Zwiększamy licznik
    # wywołań rekurencyjnych
    rcnt += 1
    v = 0
    # Jeśli zbiory SP i SX są puste,
    # to SR zawiera wierzchołki
    # kliki maksymalnej
    if s_empty(sp) and s_empty(sx):
        print("MAXIMAL CLIQUE:",end=" ")
        s_print(sr)
        print()
    else:
        # Tworzymy zbiór
        # pomocniczy PPP
        s_create(ppp, 0, n-1)
        # W PPP tworzymy sumę
        # zbiorów SP i SX
        s_union(sp, sx, ppp)
        # Zerujemy
        # licznik sąsiadów
        ncmax = 0
        # Przechodzimy przez
        # kolejne wierzchołki grafu
        for u in range(n):
            # Jeśli wierzchołek
            # jest w PPP,
            # to przetwarzamy go
            if s_isin(ppp, u):
                # Zerujemy bieżący
                # licznik sąsiadów
                nc = 0
                p = graf[u]
                # Badamy sąsiadów
                # wierzchołka v
                while p:
                    if s_isin(sp, p.v):
                        # Jeśli sąsiad
                        # w SP,
                        # zwiększamy nc
                        nc += 1
                    # Następny sąsiad
                    p = p.next
                # Jeśli liczba sąsiadów
                # w P jest większa
                # od ncmax,
                if nc >= ncmax:
                    # to zapamiętujemy
                    # wierzchołek
                    v = u
                    # oraz jego liczbę
                    # sąsiadów w P
                    ncmax = nc
        # W PPP umieszczamy zbiór SP
        s_copy(sp, ppp)
        p = graf[v]
        # Przeglądamy listę
        # sąsiadów piwota
        while p:
            # Z PPP usuwamy każdego
            # sąsiada piwota
            s_remove(ppp, p.v)
            # Następny sąsiad
            p = p.next
        # Tworzymy puste
        # zbiory pomocnicze
        s_create(rp, 0, n-1)
        s_create(pp, 0, n-1)
        s_create(xp, 0, n-1)
        # Przechodzimy przez
        # kolejne wierzchołki grafu
        for v in range(n):
            # Jeśli wierzchołek v
            # jest w zbiorze PPP, to
            # go przetwarzamy
            if s_isin(ppp, v):
                # Najpierw zerujemy
                # zbiór sąsiadów
                s_clear(sn)
                # Przeglądamy listę
                # sąsiedztwa
                # wierzchołka v
                p = graf[v]
                while p:
                    # Każdego sąsiada
                    # umieszczamy w SN
                    s_add(sn, p.v)
                    p = p.next
                # W RP tworzymy
                # zbiór SR z dodanym
                # wierzchołkiem v
                s_copy(sr, rp)
                s_add(rp, v)
                # W PP tworzymy
                # iloczyn SP i SN
                s_intersection(sp,sn,pp)
                # W XP tworzymy
                # iloczyn SX i SN
                s_intersection(sx,sn,xp)
                # Wywołanie rekurencyjne
                bron_kerbosch(rp,pp,xp)
                # Z SP usuwamy
                # wierzchołek v
                s_remove(sp, v)
                # i dodajemy go do SX
                s_add(sx, v)
        # Usuwamy zbiory pomocnicze
        del rp.t, pp.t, xp.t, ppp.t

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

sr = Set()
sp = Set()
sx = Set()
sn = Set()
rcnt = 0
# Odczytujemy liczbę
# wierzchołków i krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy puste zbiory
s_create(sr, 0, n-1)
s_create(sp, 0, n-1)
s_create(sx, 0, n-1)
s_create(sn, 0, n-1)
# Tworzymy tablicę
# list sąsiedztwa
graf = [None] * n
for i in range(m):
    # Wierzchołki krawędzi
    x = input().split()
    u = int(x[0])
    v = int(x[1])
    # Tworzymy element listy
    p = SLel()
    p.v = u
    # Element dołączamy
    # do listy sąsiadów v
    p.next = graf[v]
    graf[v] = p
    # To samo dla krawędzi
    # w drugą stronę
    p = SLel()
    p.v = v
    # Element dołączamy
    # do listy sąsiadów u
    p.next = graf[u]
    graf[u] = p
# W zbiorze SP ustawiamy
# wszystkie wierzchołki
s_full(sp)
print()
# Szukamy rekurencyjnie
# klik maksymalnych
bron_kerbosch(sr, sp, sx)
print()
print("RECURSIVE CALLS =",rcnt)
print()
# Usuwamy zbiory
del sr.t, sp.t, sx.t, sn.t
# Usuwamy tablicę
# list sąsiedztwa
for v in range(n):
    while graf[v]:
        graf[v] = graf[v].next
del graf
Wynik:
8 19
0 1
0 2
0 5
0 6
1 2
1 3
1 5
1 6
2 4
2 5
2 6
2 7
3 4
3 6
3 7
4 6
5 6
5 7
6 7

MAXIMAL CLIQUE: 2 4 6
MAXIMAL CLIQUE: 0 1 2 5 6
MAXIMAL CLIQUE: 2 5 6 7
MAXIMAL CLIQUE: 1 3 6
MAXIMAL CLIQUE: 3 4 6
MAXIMAL CLIQUE: 3 6 7

RECURSIVE CALLS = 12
 obrazek

W pierwszej wersji liczba wywołań rekurencyjnych wynosiła 52. Tutaj mamy ich 12.


do podrozdziału  do strony 

Klika największa

Algorytm Brona-Kerboscha da się łatwo przystosować do wyszukiwania kliki największej. W tym celu w tworzymy pusty zbiór RM, który będzie zapamiętywał klikę największą. Następnie w pierwszej części algorytmu Brona-Kerbosha, gdy zbiory P i X są zerowe, nie wypisujemy zbioru R, lecz sprawdzamy, czy jego liczebność jest większa od liczebności zbioru RM. Jeśli tak, to zbiór R kopiujemy do RM. Aby nie zliczać ciągle elementów zbiorów, można w osobnej zmiennej zapamiętywać ich liczebność. Gdy algorytm Brona-Kerboscha zakończy działanie, w zbiorze RM będzie największa klika grafu.

Algorytm Brona-Kerboscha znajdowania kliki największej w grafie nieskierowanym

BronKerbosch(n,graf,R,P,X,RM,rmsize)

Przy pierwszym wywołaniu zbiór RM powinien być pusty, a rmsize powinno zawierać 0. Oba te elementy mogą być globalne, aby zaoszczędzić pamięć na przekazywanie parametrów.

Wejście:

n : liczba wierzchołków w grafie; n ∈ C.
graf : zadany w dowolnie wybrany sposób, algorytm tego nie precyzuje.
R : zbiór wierzchołków, które są częściowym wynikiem znajdowania kliki.
P : zbiór wierzchołków, które są kandydatami do rozważenia.
X : zbiór wierzchołków pominiętych.
RM : zbiór dotychczasowej kliki największej.
rmsize : rozmiar dotychczasowej kliki największej; rmsize ∈ C.

Wyjście:

Zbiór RM zawiera klikę największą. W zmiennej rmsize zostaje umieszczony rozmiar tej kliki.

Elementy pomocnicze:

u,v,w : numery wierzchołków grafu; u,v,w ∈ C.
P',P",R',X',N : pomocnicze zbiory wierzchołków.
ncmax,nc : liczniki wierzchołków; ncmax,nc ∈ C.
rms : liczba wierzchołków w zbiorze R; rms ∈ C.

Lista kroków:

K01: Jeśli (P nie jest pusty) obrazek (X nie jest pusty), ; znaleziona klika maksymalna
     to idź do kroku K05
K02: rms ← rozmiar R ; wyznaczamy liczbę wierzchołków w klice maksymalnej
K03: Jeśli rms > rmsize, to ; sprawdzamy, czy obecna klika jest większa od pamiętanej.
     RMR ; Jeśli tak, to zapamiętaj klikę oraz jej nowy rozmiar
     rmsizerms
K04: Zakończ
K05: P" ← suma zbiorów P i X ; będziemy szukać piwota w sumie zbiorów P i X
K06: ncmax ← 0 ; zerujemy globalny licznik sąsiadów
K07: Dla każdego wierzchołka u w P": ; przeglądamy graf,
     wykonuj kroki K08…K10 ; biorąc pod uwagę tylko wierzchołki w P
K08:   nc ← 0 ; zerujemy bieżący licznik sąsiadów
K09:   Dla każdego sąsiada w wierzchołka u:
       wykonuj:
       Jeśli w należy do P, ; jeśli sąsiad jest w P,
       to ncnc + 1 ; to zwiększamy bieżący licznik sąsiadów
K10:   Jeśli ncncmax, to ; jeśli wierzchołek u ma nie mniej sąsiadów niż jest w ncmax,
       vu ; to zapamiętujemy ten wierzchołek i zapamiętujemy liczbę jego sąsiadów
       ncmaxnc
K11: P" ← P
K12: Dla każdego sąsiada u wierzchołka v: ; tworzymy zbiór P bez sąsiadów piwota
     wykonuj:
     P" ← P"-u
K13: Dla każdego wierzchołka v w P":
     wykonuj K14…K21
K14:   Zeruj zbiór N
K15:   Dla każdego sąsiada u wierzchołka v: ; tworzymy zbiór sąsiadów
       wykonuj:
       NN+u
K16:   R' ← R+v ; w R' tworzymy zbiór R z dodanym wierzchołkiem v
K17:   P' ← iloczyn zbiorów P i N ; z P' usuwamy wierzchołki, które nie są sąsiadami v
K18:   X' ← iloczyn zbiorów X i N ; z X' usuwamy wierzchołki, które nie są sąsiadami v
K19:   BronKerbosch(n,graf,R',P',X') ; wywołanie rekurencyjne
K20:   PP - v ; ze zbioru P usuwamy wierzchołek v
K21:   RR + v ; do zbioru R dodajemy wierzchołek v
K22: Zakończ

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 wyszukuje w nim wszystkie kliki maksymalne za pomocą algorytmu Brona-Kerboscha. Zbiory zostały zaimplementowane w mapach bitowych.
Program dodatkowo wypisuje liczbę wywołań rekurencyjnych algorytmu Brona-Kerboscha dla celów porównawczych.
Dane przykładowe (przekopiuj do schowka i wklej do okna konsoli):
obrazek 8 19
0 1
0 2
0 5
0 6
1 2
1 3
1 5
1 6
2 4
2 5
2 6
2 7
3 4
3 6
3 7
4 6
5 6
5 7
6 7
Pascal
// Wyszukiwanie kliki największej
// Data: 15.07.2014
// (C)2014 mgr Jerzy Wałaszek
//-------------------------------

program cliques;

// Definicja elementu
// listy sąsiedztwa
type
  PSLel = ^SLel;
  SLel = record
    // Następny element listy
    next : PSLel;
    // Wierzchołek docelowy
    v : integer;
  end;

// Definicja struktury zbioru
  SSet =  record
    vmin : integer;
    nmax : integer;
    T : array of boolean;
  end;

// Zmienne globalne
var
  // Liczba wierzchołków
  // i krawędzi grafu
  n, m : integer;
  // Tablica list sąsiedztwa
  graf : array of PSLel;
  // Zbiór sąsiadów
  // i zbiór kliki największej
  SN, RM : SSet;
  // Liczba wierzchołków
  // w klice największej
  rmsize: integer;

// Obsługa zbiorów

// Procedura dodaje
// element x do zbioru A
//----------------------
procedure s_add(var A : SSet;
                    x : integer);
var
  i : integer;
begin
  // Obliczamy indeks
  i := x - A.vmin;
  // Ustawiamy element
  A.T[i] := true;
end;

// Procedura usuwa
// element x ze zbioru
//--------------------
procedure s_remove(var A : SSet;
                       x : integer);
var
  i : integer;
begin
  // Obliczamy indeks
  i := x - A.vmin;
  // Zerujemy element
  A.T[i] := false;
end;

// Procedura usuwa wszystkie
// elementy ze zbioru
//--------------------------
procedure s_clear(var A : SSet);
var
  i : integer;
begin
  // Zerujemy tablicę
  for i := 0 to A.nmax do
    A.T[i] := false;
end;

// Procedura tworzy zbiór
//-----------------------
procedure s_create(var A : SSet;
              vmin, vmax : integer);
begin
  // Wypełniamy
  // strukturę danymi
  A.vmin := vmin;
  // Indeks ostatniego
  // elementu tablicy
  A.nmax := vmax - vmin;
  // Tworzymy dynamicznie
  // tablicę logiczną
  SetLength(A.T, A.nmax + 1);
  // Tworzymy zbiór pusty
  s_clear(A);
end;

// Procedura wyznacza
// sumę zbiorów A i B
//-------------------
procedure s_union
          (var A, B, C : SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    C.T[i] := A.T[i] or B.T[i];
end;

// Procedura wyznacza
// część wspólną zbiorów A i B
//----------------------------
procedure s_intersection
          (var A, B, C :SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    C.T[i] := A.T[i] and B.T[i];
end;

// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
procedure s_copy(var A, B : SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    B.T[i] := A.T[i];
end;

// Procedura ustawia
// zbiór pełny
procedure s_full(var A : SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    A.T[i] := true;
end;

// Funkcja sprawdza,
// czy zbiór A jest pusty
// true  - jest
// false - nie jest
//-----------------------
function s_empty(var A : SSet)
                       : boolean;
var
  i : integer;
  m : boolean;
begin
  // Pobieramy bity do m
  m := A.T[0];
  for i := 1 to A.nmax do
    // Sumujemy logicznie bity
    m := m or A.T[i];
  s_empty := (m = false);
end;

// Funkcja bada,
// czy element x
// należy do zbioru A
// true  - tak, należy
// false - nie, nie należy
//------------------------
function s_isin(var A : SSet;
                    x : integer)
                      : boolean;
var
  i : integer;
begin
  // Obliczamy indeks
  i := x - A.vmin;
  s_isin := A.T[i]
end;

// Procedura wyświetla
// elementy zbioru A
//--------------------
procedure s_print(var A : SSet);
var
  i : integer;
begin
  for i := 0 to A.nmax do
    if A.T[i] then
        write(i + A.vmin,' ');
end;

// Funkcja oblicza
// liczbę elementów w A
//---------------------
function s_size(var A : SSet)
                      : integer;
var
  i, c : integer;
begin
  // Zerujemy licznik
  c := 0;
  // Przechodzimy przez
  // kolejne komórki
  for i := 0 to A.nmax do
    if A.T[i] then inc(c);
   s_size := c;
end;

// Procedura wyszukuje
// klikę największą
//--------------------
procedure BronKerbosch
          (var SR, SP, SX : SSet);
var
  RP, PP, XP, PPP : SSet;
  p : PSLel;
  v, u, ncmax, nc, rms : integer;
begin
  // Jeśli zbiory
  // SP i SX są puste,
  // to SR zawiera wierzchołki
  // kliki maksymalnej
  if s_empty(SP) and
     s_empty(SX) then
  begin
    // Wyznaczamy liczbę
    // wierzchołków w klice
    rms := s_size(SR);
    // Jeśli mamy większą klikę
    // od dotychczas znalezionej,
    if rms > rmsize then
    begin
      // to zapamiętujemy ją
      s_copy(SR, RM);
      // Zapamiętujemy również
      // jej rozmiar
      rmsize := rms;
    end;
  end
  else
  begin
    // Tworzymy zbiór
    // pomocniczy PPP
    s_create(PPP, 0, n-1);
    // W PPP tworzymy sumę
    // zbiorów SP i SX
    s_union(SP, SX, PPP);
    // Zerujemy licznik
    // sąsiadów
    ncmax := 0;
    // Przechodzimy przez
    // kolejne wierzchołki
    // grafu
    for u := 0 to n-1 do
      // Jeśli wierzchołek jest
      // w PPP, to przetwarzamy go
      if s_isin(PPP, u) then
      begin
        // Zerujemy bieżący
        // licznik sąsiadów
        nc := 0;
        // Badamy sąsiadów
        // wierzchołka v
        p := graf[u];
        while p <> nil do
        begin
          // Jeśli sąsiad w SP,
          // zwiększamy nc
          if s_isin(SP, p^.v) then
            inc(nc);
          // Następny sąsiad
          p := p^.next;
        end;
        // Jeśli liczba sąsiadów
        // w P jest większa
        // od ncmax,
        if nc >= ncmax then
        begin
          // to zapamiętujemy
          // wierzchołek
          v := u;
          // oraz jego liczbę
          // sąsiadów w P
          ncmax := nc;
        end;
      end;
     // W PPP umieszczamy
     // zbiór SP
     s_copy(SP, PPP);
     p := graf[v];
     // przeglądamy listę
     // sąsiadów piwota
     while p <> nil do
     begin
       s_remove(PPP, p^.v);
       // Następny sąsiad
       p := p^.next;
     end;
     // Tworzymy puste
     // zbiory pomocnicze
     s_create(RP, 0, n-1);
     s_create(PP, 0, n-1);
     s_create(XP, 0, n-1);
     // Przechodzimy przez
     // kolejne wierzchołki
     // grafu
     for v := 0 to n-1 do
       // Jeśli wierzchołek v
       // jest w zbiorze PPP, to
       // go przetwarzamy
       if s_isin(PPP, v) then
       begin
         // Najpierw zerujemy
         // zbiór sąsiadów
         s_clear(SN);
         // Przeglądamy listę
         // sąsiedztwa wierzchołka v
         p := graf[v];
         while p <> nil do
         begin
           // Każdego sąsiada
           // umieszczamy w SN
           s_add(SN, p^.v);
           p := p^.next;
         end;
         // W RP tworzymy
         // zbiór SR z dodanym
         // wierzchołkiem v
         s_copy(SR, RP);
         s_add(RP, v);
         // W PP tworzymy
         // iloczyn SP i SN
         s_intersection(SP, SN, PP);
         // W XP tworzymy
         // iloczyn SX i SN
         s_intersection(SX, SN, XP);
         // Wywołanie rekurencyjne
         BronKerbosch(RP, PP, XP);
         // Z SP usuwamy
         // wierzchołek v
         s_remove(SP, v);
         // i dodajemy go do SX
         s_add(SX, v);
       end;
    // Usuwamy zbiory pomocnicze
    SetLength(RP.T, 0);
    SetLength(PP.T, 0);
    SetLength(XP.T, 0);
    SetLength(PPP.T, 0);
  end;
end;

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

var
  SR, SP, SX : SSet;
  i, u, v : integer;
  p, r : PSLel;
begin
  // Odczytujemy liczbę
  // wierzchołków i krawędzi
  read(n, m);
  // Tworzymy puste zbiory
  s_create(SR, 0, n-1);
  s_create(SP, 0, n-1);
  s_create(SX, 0, n-1);
  s_create(SN, 0, n-1);
  // Pusty zbiór
  // kliki największej
  s_create(RM, 0, n-1);
  // Liczba wierzchołków
  // w klice największej
  rmsize := 0;
  // Tworzymy tablicę
  // list sąsiedztwa
  SetLength(graf, n);
  // Zerujemy listy sąsiedztwa
  for i := 0 to n-1 do
    graf[i] := nil;
  for i := 1 to m do
  begin
    // Wierzchołki krawędzi
    read(u, v);
    // Tworzymy element listy
    new(p);
    p^.v := u;
    // Element dołączamy
    // do listy sąsiadów v
    p^.next := graf[v];
    graf[v] := p;
    // To samo dla krawędzi
    // w drugą stronę
    new(p);
    p^.v := v;
    // Element dołączamy
    // do listy sąsiadów u
    p^.next := graf[u];
    graf[u] := p;
  end;
  // W zbiorze SP ustawiamy
  // wszystkie wierzchołki
  s_full(SP);
  writeln;
  // Szukamy rekurencyjnie
  // kliki największej
  BronKerbosch(SR, SP, SX);
  write('MAXIMUM CLIQUE: ');
  s_print(RM);
  writeln;
  writeln('NUMBER OF VERTICES'+
          ' IN MAXIMUM CLIQUE = ',
          rmsize);
  writeln;
  // Usuwamy zbiory
  SetLength(SR.T, 0);
  SetLength(SP.T, 0);
  SetLength(SX.T, 0);
  SetLength(SN.T, 0);
  SetLength(RM.T, 0);
  // Usuwamy tablicę
  // list sąsiedztwa
  for v := 0 to n-1 do
  begin
    p := graf[v];
    while p <> nil do
    begin
      r := p;
      p := p^.next;
      dispose(r);
    end;
  end;
  SetLength(graf, 0);
end.
C++
// Wyszukiwanie kliki
// największej
// Data: 15.07.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------

#include <iostream>
#include <iomanip>

using namespace std;

// Definicja elementu
// listy sąsiedztwa
struct SLel
{
  // Następny element listy
  SLel *next;
  // Wierzchołek docelowy
  int v;
};

// Definicja
// struktury zbioru
struct Set
{
  int vmin, nmax;
  bool *T;
};

// Zmienne globalne

// Liczba wierzchołków
// i krawędzi grafu
int n, m;
// Tablica
// list sąsiedztwa
SLel ** graf;
// Zbiór sąsiadów
// i zbiór kliki największej
Set SN, RM;
// Liczba wierzchołków
// w klice największej
int rmsize;

// Obsługa zbiorów
//----------------

// Procedura dodaje
// element x do zbioru A
//----------------------
void s_add(Set & A, int x)
{
  // Obliczamy indeks
  int i = x - A.vmin;
  // Ustawiamy bit na 1
  A.T[i] = true;
}

// Procedura usuwa
// element x ze zbioru
//--------------------
void s_remove(Set & A, int x)
{
  // Obliczamy indeks
  int i = x - A.vmin;
  // Ustawiamy bit na 0
  A.T[i] = false;
}

// Procedura usuwa wszystkie
// elementy ze zbioru
//--------------------------
void s_clear(Set & A)
{
  // Zerujemy tablicę
  for(int i = 0; i <= A.nmax; i++)
    A.T[i] = false;
}

// Procedura tworzy zbiór
//-----------------------
void s_create(Set & A,
              int vmin,
              int vmax)
{
  // Wypełniamy strukturę danymi
  A.vmin = vmin;
  // Indeks ostatniego
  // elementu tablicy
  A.nmax = vmax - vmin;
  // Tworzymy dynamicznie
  // tablicę logiczną
  A.T = new bool [A.nmax+1];
  // Tworzymy zbiór pusty
  s_clear(A);
}

// Procedura wyznacza
// sumę zbiorów A i B
//-------------------
void s_union(Set & A,
             Set & B,
             Set & C)
{
  for(int i = 0; i <= A.nmax; i++)
    C.T[i] = A.T[i] || B.T[i];
}

// Procedura wyznacza część
// wspólną zbiorów A i B
//-------------------------
void s_intersection(Set & A,
                    Set & B,
                    Set & C)
{
  for(int i = 0; i <= A.nmax; i++)
    C.T[i] = A.T[i] && B.T[i];
}

// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
void s_copy(Set & A, Set & B)
{
  for(int i = 0; i <= A.nmax; i++)
    B.T[i] = A.T[i];
}

// Procedura ustawia
// zbiór pełny
//-------------------
void s_full(Set & A)
{
  for(int i = 0; i <= A.nmax; i++)
    A.T[i] = true;
}

// Funkcja sprawdza,
// czy zbiór A jest pusty
// true  - tak, jest pusty
// false - nie, nie jest pusty
//----------------------------
bool s_empty(Set A)
{
  // Pobieramy bity do m
  bool m = A.T[0];
  // Sumujemy logicznie bity
  for(int i = 1; i <= A.nmax; i++)
    m = m || A.T[i];
  return !m;
}

// Funkcja bada, czy element
// x należy do zbioru A
// true  - tak, należy
// false - nie, nie należy
//--------------------------
bool s_isin(Set & A, int x)
{
  // Obliczamy indeks
  int i = x - A.vmin;
  return A.T[i];
}

// Procedura wyświetla
// elementy zbioru A
//--------------------
void s_print(Set A)
{
  int i;

  for(i = 0; i <= A.nmax; i++)
    if(A.T[i])
      cout << i + A.vmin << " ";
}

// Funkcja oblicza
// liczbę elementów w A
//---------------------
int s_size(Set & A)
{
  // Zerujemy licznik
  int c = 0;
  // Przechodzimy przez
  // kolejne komórki
  for(int i = 0; i <= A.nmax; i++)
    // Zliczamy bity równe 1
    if(A.T[i]) c++;
  return c;
}

// Procedura wyszukuje
// klikę największą
//--------------------
void BronKerbosch(Set & SR,
                  Set & SP,
                  Set & SX)
{
  Set RP, PP, XP, PPP;
  SLel * p;
  int v, u, ncmax, nc, rms;

  // Jeśli zbiory
  // SP i SX są puste,
  // to SR zawiera wierzchołki
  // kliki maksymalnej
  if(s_empty(SP) &&
     s_empty(SX))
  {
    // Wyznaczamy liczbę
    // wierzchołków w klice
    rms = s_size(SR);
    // Jeśli mamy większą
    // klikę od dotychczas
    // znalezionej,
    if(rms > rmsize)
    {
      // to zapamiętujemy ją
      s_copy(SR, RM);
      // Zapamiętujemy
      // również jej rozmiar
      rmsize = rms;
    }
  }
  else
  {
    // Tworzymy zbiór
    // pomocniczy PPP
    s_create(PPP, 0, n-1);
    // W PPP tworzymy sumę
    // zbiorów SP i SX
    s_union(SP, SX, PPP);
    // Zerujemy
    // licznik sąsiadów
    ncmax = 0;
    // Przechodzimy przez
    // kolejne wierzchołki
    // grafu
    for(u = 0; u < n; u++)
      // Jeśli wierzchołek
      // jest w PPP, to
      // przetwarzamy go
      if(s_isin(PPP, u))
      {
        // Zerujemy bieżący
        // licznik sąsiadów
        nc = 0;
        // Badamy sąsiadów
        // wierzchołka v
        for(p = graf[u];
            p; p = p->next)
          // Jeśli sąsiad w SP,
          // zwiększamy nc
          if(s_isin(SP, p->v))
            nc++;
        // Jeśli liczba sąsiadów
        // w P jest większa
        // od ncmax,
        if(nc >= ncmax)
        {
          // to zapamiętujemy
          // wierzchołek
          v = u;
          // oraz liczbę jego
          // sąsiadów w P
          ncmax = nc;
        }
      }
    // W PPP umieszczamy
    // zbiór SP
    s_copy(SP, PPP);
    // przeglądamy listę
    // sąsiadów piwota
    for(p = graf[v];
        p; p = p->next)
      // Z PPP usuwamy każdego
      // sąsiada piwota
      s_remove(PPP, p->v);
    // Tworzymy puste
    // zbiory pomocnicze
    s_create(RP, 0, n-1);
    s_create(PP, 0, n-1);
    s_create(XP, 0, n-1);
    // Przechodzimy przez
    // kolejne wierzchołki
    // grafu
    for(v = 0; v < n; v++)
      // Jeśli wierzchołek v
      // jest w zbiorze PPP, to
      // go przetwarzamy
      if(s_isin(PPP, v))
      {
        // Najpierw zerujemy
        // zbiór sąsiadów
        s_clear(SN);
        // Przeglądamy listę
        // sąsiedztwa
        // wierzchołka v
        for(p = graf[v];
            p; p = p->next)
          // Każdego sąsiada
          // umieszczamy w SN
          s_add(SN, p->v);
        // W RP tworzymy zbiór SR
        // z dodanym
        // wierzchołkiem v
        s_copy(SR, RP);
        s_add(RP, v);
        // W PP tworzymy
        // iloczyn SP i SN
        s_intersection(SP, SN, PP);
        // W XP tworzymy
        // iloczyn SX i SN
        s_intersection(SX, SN, XP);
        // Wywołanie rekurencyjne
        BronKerbosch(RP, PP, XP);
        // Z SP usuwamy
        // wierzchołek v
        s_remove(SP, v);
        // i dodajemy
        // go do SX
        s_add(SX, v);
      }
    // Usuwamy zbiory pomocnicze
    delete [] RP.T;
    delete [] PP.T;
    delete [] XP.T;
    delete [] PPP.T;
  }
}

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

int main()
{
  Set SR, SP, SX;
  int i, u, v;
  SLel *p, *r;

  // Odczytujemy liczbę
  // wierzchołków i krawędzi
  cin >> n >> m;
  // Tworzymy puste zbiory
  s_create(SR, 0, n-1);
  s_create(SP, 0, n-1);
  s_create(SX, 0, n-1);
  s_create(SN, 0, n-1);
  // Pusty zbiór
  // kliki największej
  s_create(RM, 0, n-1);
  // Liczba wierzchołków
  // w klice największej
  rmsize = 0;
  // Tworzymy tablicę
  // list sąsiedztwa
  graf = new SLel * [n];
  // Zerujemy
  // listy sąsiedztwa
  for(i = 0; i < n; i++)
    graf[i] = nullptr;
  for(i = 0; i < m; i++)
  {
    // Wierzchołki krawędzi
    cin >> u >> v;
    // Tworzymy element listy
    p = new SLel;
    p->v = u;
    // Element dołączamy
    // do listy sąsiadów v
    p->next = graf[v];
    graf[v] = p;
    // To samo dla krawędzi
    // w drugą stronę
    p = new SLel;
    p->v = v;
    // Element dołączamy
    // do listy sąsiadów u
    p->next = graf[u];
    graf[u] = p;
  }
  // W zbiorze SP
  // ustawiamy
  // wszystkie wierzchołki
  s_full(SP);
  cout << endl;
  // Szukamy rekurencyjnie
  // kliki największej
  BronKerbosch(SR, SP, SX);
  cout << "MAXIMUM CLIQUE: ";
  s_print(RM);
  cout << endl
       << "NUMBER OF VERTICES"
          " IN MAXIMUM CLIQUE = "
       << rmsize << endl << endl;
  // Usuwamy zbiory
  delete [] SR.T;
  delete [] SP.T;
  delete [] SX.T;
  delete [] SN.T;
  delete [] RM.T;
  // Usuwamy tablicę
  // list sąsiedztwa
  for(v = 0; v < n; v++)
  {
    p = graf[v];
    while(p)
    {
      r = p;
      p = p->next;
      delete r;
    }
  }
  delete [] graf;
  return 0;
}
Basic
' Wyszukiwanie kliki największej
' Data: 15.07.2014
' (C)2014 mgr Jerzy Wałaszek
'-------------------------------

' Definicja elementu
' listy sąsiedztwa
Type SLel
  ' Następny element listy
  next As SLel Ptr
  ' Wierzchołek docelowy
  v As Integer
End Type

' Definicja struktury zbioru
Type Set
  vmin As Integer
  nmax As Integer
  T As Boolean Ptr
End Type

' Zmienne globalne

' Liczba wierzchołków
' i krawędzi grafu
Dim Shared As Integer n, m
' Tablica list sąsiedztwa
Dim Shared As SLel Ptr Ptr graf
' Zbiór sąsiadów
' i zbiór kliki największej
Dim Shared As Set SN, RM
' Liczba wierzchołków
' w klice największej
Dim Shared As Integer rmsize

' Obsługa zbiorów
'----------------
' Procedura dodaje
' element x do zbioru A
'----------------------
Sub s_add(ByRef A As Set, _
          ByVal x As Integer)
  Dim As Integer i

  ' Obliczamy indeks
  i = x - A.vmin
  ' Ustawiamy bit na 1
  A.T[i] = True
End Sub

' Procedura usuwa
' element x ze zbioru
'--------------------
Sub s_remove(ByRef A As Set, _
             ByVal x As Integer)
  Dim As Integer i

  ' Obliczamy indeks
  i = x - A.vmin
  ' Ustawiamy bit na 0
  A.T[i] = False
End Sub

' Procedura usuwa
' wszystkie elementy
' ze zbioru
'-------------------
Sub s_clear(ByRef A As Set)
  Dim As Integer i
  
  ' Zerujemy tablicę
  For i = 0 To A.nmax
    A.T[i] = False
  Next
End Sub

' Procedura tworzy zbiór
'-----------------------
Sub s_create(_
          ByRef A As Set, _
          ByVal vmin As Integer,_
          ByVal vmax As Integer)
  ' Wypełniamy strukturę danymi
  A.vmin = vmin
  ' Indeks ostatniego
  ' elementu tablicy
  A.nmax = vmax - vmin
  ' Tworzymy dynamicznie
  ' tablicę mapy bitowej
  A.T = New Boolean [A.nmax + 1]
  ' Tworzymy zbiór pusty
  s_clear(A)
End Sub

' Procedura wyznacza
' sumę zbiorów A i B
'-------------------
Sub s_union(ByRef A As Set,_
            ByRef B As Set,_
            ByRef C As Set)
  Dim As Integer i

  For i = 0 To A.nmax
  	 C.T[i] = A.T[i] Or B.T[i]
  Next
End Sub

' Procedura wyznacza
' część wspólną
' zbiorów A i B
'-------------------
Sub s_intersection(ByRef A As Set,_
                   ByRef B As Set,_
                   ByRef C As Set)
  Dim As Integer i

  For i = 0 To A.nmax
    C.T[i] = A.T[i] And B.T[i]
  Next
End Sub

' Procedura kopiuje
' zbiór A do zbioru B
'--------------------
Sub s_copy(ByRef A As Set, _
           ByRef B As Set)
  Dim As Integer i
  
  For i = 0 To A.nmax
    B.T[i] = A.T[i]
  Next
End Sub

' Procedura ustawia
' zbiór pełny
'------------------
Sub s_full(ByRef A As Set)
  Dim As Integer i
  
  For i = 0 To A.nmax
    A.T[i] = True
  Next
End Sub

' Funkcja sprawdza,
' czy zbiór A jest pusty
' true  - tak, jest pusty
' false - nie, nie jest pusty
'----------------------------
Function s_empty(ByRef A As Set) _
                         As Boolean
  Dim As Integer i
  ' Pobieramy bity do m
  Dim As Boolean m = A.T[0]
  
  For i = 1 To A.nmax
    ' Sumujemy logicznie bity
    m = m Or A.T[i]
  Next
  Return Not m
End Function

' Funkcja bada,
' czy element x
' należy do zbioru A
' true  - tak, należy
' false - nie, nie należy
'------------------------
Function s_isin(_
         ByRef A As Set, _
         ByVal x As Integer)_
                 As Boolean
  ' Obliczamy indeks
  Dim As Integer i = x - A.vmin
  Return A.T[i]
End Function

' Procedura wyświetla
' elementy zbioru A
'--------------------
Sub s_print(ByRef A As Set)
  Dim As Integer i

  For i = 0 To A.nmax
    If A.T[i] Then _
      Print Using "& ";i + A.vmin;
  Next
End Sub

' Funkcja oblicza
' liczbę elementów w A
'---------------------
Function s_size(ByRef A As Set)_
                        As Integer
  ' Zerujemy licznik
  Dim As Integer i, c = 0
  For i = 0 To A.nmax
    If A.T[i] then c += 1
  Next
  Return c
End Function

' Procedura wyszukuje
' klikę największą
'--------------------
Sub BronKerbosch(ByRef SR As Set,_
                 ByRef SP As Set,_
                 ByRef SX As Set)
  Dim As Set RP,PP,XP,PPP
  Dim As SLel Ptr p
  Dim As Integer v,u,ncmax,nc,rms

  ' Jeśli zbiory
  ' SP i SX są puste,
  ' to SR zawiera
  ' wierzchołki
  ' kliki maksymalnej
  If s_empty(SP) AndAlso _
     s_empty(SX) Then 
    ' Wyznaczamy liczbę
    ' wierzchołków w klice
    rms = s_size(SR)
    ' Jeśli mamy większą
    ' klikę od dotychczas
    ' znalezionej, 
    If rms > rmsize Then
      ' to zapamiętujemy ją
      s_copy(SR, RM)
      ' Zapamiętujemy
      ' również jej rozmiar
      rmsize = rms
    End If
  Else
    ' Tworzymy zbiór
    ' pomocniczy PPP
    s_create(PPP, 0, n-1)
    ' W PPP tworzymy
    ' sumę zbiorów SP i SX
    s_union(SP, SX, PPP)
    ' Zerujemy
    ' licznik sąsiadów
    ncmax = 0
    ' Przechodzimy przez
    ' kolejne wierzchołki
    ' grafu
    For u = 0 To n-1
      ' Jeśli wierzchołek
      ' jest w PPP,
      ' to przetwarzamy go
      If s_isin(PPP, u) Then
        ' Zerujemy bieżący
        ' licznik sąsiadów
        nc = 0
        p = graf[u]
        ' Badamy sąsiadów
        ' wierzchołka v
        While p <> 0
          ' Jeśli sąsiad w SP,
          ' zwiększamy nc
          If s_isin(SP,p->v) Then _
            nc += 1
          ' Następny sąsiad
          p = p->next
        Wend
        ' Jeśli liczba
        ' sąsiadów w P jest
        ' większa od ncmax, 
        If nc >= ncmax Then
          ' to zapamiętujemy
          ' wierzchołek
          v = u
          ' oraz jego liczbę
          ' sąsiadów w P
          ncmax = nc
        End If
      End If
    Next
    ' W PPP umieszczamy
    ' zbiór SP
    s_copy(SP, PPP)
    p = graf[v]
    ' Przeglądamy listę
    ' sąsiadów piwota
    While p <> 0
      ' Z PPP usuwamy
      ' każdego sąsiada
      ' piwota
      s_remove(PPP, p->v)
      ' Następny sąsiad
      p = p->next
    Wend
    ' Tworzymy puste
    ' zbiory pomocnicze
    s_create(RP, 0, n-1)
    s_create(PP, 0, n-1)
    s_create(XP, 0, n-1)
    ' Przechodzimy przez
    ' kolejne wierzchołki
    ' grafu
    For v = 0 To n-1
      ' Jeśli wierzchołek v
      ' jest w zbiorze PPP, to
      ' go przetwarzamy
      If s_isin(PPP, v) Then
        ' Najpierw zerujemy
        ' zbiór sąsiadów
        s_clear(SN)
        ' Przeglądamy listę
        ' sąsiedztwa
        ' wierzchołka v
        p = graf[v]
        While p <> 0
          ' Każdego sąsiada
          ' umieszczamy w SN
          s_add(SN, p->v)
          p = p->next
        Wend
        ' W RP tworzymy
        ' zbiór SR z dodanym
        ' wierzchołkiem v
        s_copy(SR, RP)
        s_add(RP, v)
        ' W PP tworzymy
        ' iloczyn SP i SN
        s_intersection(SP, SN, PP)
        ' W XP tworzymy
        ' iloczyn SX i SN
        s_intersection(SX, SN, XP)
        ' Wywołanie rekurencyjne
        BronKerbosch(RP, PP, XP)
        ' Z SP usuwamy
        ' wierzchołek v
        s_remove(SP, v)
        ' i dodajemy go do SX
        s_add(SX, v)
      End If
    Next
    ' Usuwamy
    ' zbiory pomocnicze
    Delete [] RP.T
    Delete [] PP.T
    Delete [] XP.T
    Delete [] PPP.T
  End If
End Sub

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

Dim As Set SR, SP, SX
Dim As integer i, u, v
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 puste zbiory
s_create(SR, 0, n-1)
s_create(SP, 0, n-1)
s_create(SX, 0, n-1)
s_create(SN, 0, n-1)
' Pusty zbiór
' kliki największej
s_create(RM, 0, n-1)
' Liczba wierzchołków
' w klice największej
rmsize = 0
' Tworzymy tablicę
' list sąsiedztwa
graf = New SLel Ptr [n]
For i = 0 To n-1
  ' Zerujemy
  ' listy sąsiedztwa
  graf[i] = 0
Next
For i = 1 To m
  ' Wierzchołki krawędzi
  Input #1, u, v
  ' Tworzymy
  ' element listy
  p = New SLel
  p->v = u
  ' Element dołączamy
  ' do listy sąsiadów v
  p->next = graf[v]
  graf[v] = p
  ' To samo dla krawędzi
  ' w drugą stronę
  p = New SLel
  p->v = v
  ' Element dołączamy
  ' do listy sąsiadów u
  p->next = graf[u]
  graf[u] = p
Next
Close #1
' W zbiorze SP
' ustawiamy wszystkie
' wierzchołki
s_full(SP)
Print
' Szukamy rekurencyjnie
' kliki największej
BronKerbosch(SR, SP, SX)
Print "MAXIMUM CLIQUE: ";
s_print(RM)
Print
Print "NUMBER OF VERTICES"+_
      " IN MAXIMUM CLIQUE =";_
      rmsize
Print
' Usuwamy zbiory
Delete [] SR.T
Delete [] SP.T
Delete [] SX.T
Delete [] SN.T
Delete [] RM.T
' Usuwamy tablicę
' list sąsiedztwa
For v = 0 To n-1
  p = graf[v]
  While p <> 0
    r = p
    p = p->next
    Delete r
  Wend
Next
Delete [] graf
End
Python (dodatek)
# Wyszukiwanie kliki największej
# Data: 27.03.2025
# (C)2025 mgr Jerzy Wałaszek
#-------------------------------

# Klasa elementu
# listy sąsiedztwa
class SLel:
    def __init__(self):
        # Następny element listy
        self.next = None
        # Wierzchołek docelowy
        self.v = 0


# Klasa struktury zbioru
class Set:
    def __init__(self):
        self.vmin = 0
        self.nmax = 0
        self.t = []


# Obsługa zbiorów
#----------------
# Procedura dodaje
# element x do zbioru A
def s_add(a,x):
    # Obliczamy indeks
    i = x - a.vmin
    # Ustawiamy bit na 1
    a.t[i] = True

# Procedura usuwa
# element x ze zbioru
def s_remove(a,x):
    # Obliczamy indeks
    i = x - a.vmin
    # Ustawiamy bit na 0
    a.t[i] = False

# Procedura usuwa
# wszystkie elementy
# ze zbioru
def s_clear(a):
    # Zerujemy tablicę
    a.t = [False] * (a.nmax + 1)

# Procedura tworzy zbiór
def s_create(a,vmin,vmax):
    # Wypełniamy strukturę danymi
    a.vmin = vmin
    # Indeks ostatniego
    # elementu tablicy
    a.nmax = vmax - vmin
    # Tworzymy dynamicznie
    # tablicę mapy bitowej
    a.t = [False] * (a.nmax + 1)

# Procedura wyznacza
# sumę zbiorów A i B
def s_union(a,b,c):
    for i in range(a.nmax + 1):
        c.t[i] = a.t[i] or b.t[i]

# Procedura wyznacza
# część wspólną
# zbiorów A i B
def s_intersection(a,b,c):
    for i in range(a.nmax + 1):
        c.t[i] = a.t[i] and b.t[i]

# Procedura kopiuje
# zbiór A do zbioru B
def s_copy(a,b):
    b.t = a.t[:]

# Procedura ustawia
# zbiór pełny
def s_full(a):
    a.t = [True] * (a.nmax + 1)

# Funkcja sprawdza,
# czy zbiór A jest pusty
# true  - tak, jest pusty
# false - nie, nie jest pusty
def s_empty(a):
    # Pobieramy bity do m
    m = a.t[0]
    for i in range(1, a.nmax + 1):
        # Sumujemy logicznie bity
        m = m or a.t[i]
    return not m

# Funkcja bada,
# czy element x
# należy do zbioru A
# true  - tak, należy
# false - nie, nie należy
def s_isin(a,x):
    # Obliczamy indeks
    i = x - a.vmin
    return a.t[i]

# Procedura wyświetla
# elementy zbioru A
def s_print(a):
    for i in range(a.nmax + 1):
        if a.t[i]:
            print(i + a.vmin, end=" ")

# Funkcja oblicza
# liczbę elementów w A
def s_size(a):
    # Zerujemy licznik
    c = 0
    for i in range(a.nmax + 1):
        if a.t[i]: c += 1
    return c

# Procedura wyszukuje
# klikę największą
#--------------------
def bron_kerbosch(sr,sp,sx):
    global n,rmsize,graf
    rp  = Set()
    pp  = Set()
    xp  = Set()
    ppp = Set()
    v = 0
    # Jeśli zbiory
    # SP i SX są puste,
    # to SR zawiera
    # wierzchołki
    # kliki maksymalnej
    if s_empty(sp) and s_empty(sx):
        # Wyznaczamy liczbę
        # wierzchołków w klice
        rms = s_size(sr)
        # Jeśli mamy większą
        # klikę od dotychczas
        # znalezionej, 
        if rms > rmsize:
            # to zapamiętujemy ją
            s_copy(sr, rm)
            # Zapamiętujemy
            # również jej rozmiar
            rmsize = rms
    else:
        # Tworzymy zbiór
        # pomocniczy PPP
        s_create(ppp, 0, n-1)
        # W PPP tworzymy
        # sumę zbiorów SP i SX
        s_union(sp, sx, ppp)
        # Zerujemy
        # licznik sąsiadów
        ncmax = 0
        # Przechodzimy przez
        # kolejne wierzchołki
        # grafu
        for u in range(n):
            # Jeśli wierzchołek
            # jest w PPP,
            # to przetwarzamy go
            if s_isin(ppp, u):
                # Zerujemy bieżący
                # licznik sąsiadów
                nc = 0
                p = graf[u]
                # Badamy sąsiadów
                # wierzchołka v
                while p:
                    # Jeśli sąsiad w SP,
                    # zwiększamy nc
                    if s_isin(sp,p.v):
                        nc += 1
                    # Następny sąsiad
                    p = p.next
                # Jeśli liczba
                # sąsiadów w P jest
                # większa od ncmax, 
                if nc >= ncmax:
                    # to zapamiętujemy
                    # wierzchołek
                    v = u
                    # oraz jego liczbę
                    # sąsiadów w P
                    ncmax = nc
        # W PPP umieszczamy
        # zbiór SP
        s_copy(sp, ppp)
        p = graf[v]
        # Przeglądamy listę
        # sąsiadów piwota
        while p:
            # Z PPP usuwamy
            # każdego sąsiada
            # piwota
            s_remove(ppp, p.v)
            # Następny sąsiad
            p = p.next
        # Tworzymy puste
        # zbiory pomocnicze
        s_create(rp, 0, n-1)
        s_create(pp, 0, n-1)
        s_create(xp, 0, n-1)
        # Przechodzimy przez
        # kolejne wierzchołki
        # grafu
        for v in range(n):
            # Jeśli wierzchołek v
            # jest w zbiorze PPP, to
            # go przetwarzamy
            if s_isin(ppp, v):
                # Najpierw zerujemy
                # zbiór sąsiadów
                s_clear(sn)
                # Przeglądamy listę
                # sąsiedztwa
                # wierzchołka v
                p = graf[v]
                while p:
                    # Każdego sąsiada
                    # umieszczamy w SN
                    s_add(sn, p.v)
                    p = p.next
                # W RP tworzymy
                # zbiór SR z dodanym
                # wierzchołkiem v
                s_copy(sr, rp)
                s_add(rp, v)
                # W PP tworzymy
                # iloczyn SP i SN
                s_intersection(sp,sn,pp)
                # W XP tworzymy
                # iloczyn SX i SN
                s_intersection(sx,sn,xp)
                # Wywołanie rekurencyjne
                bron_kerbosch(rp,pp,xp)
                # Z SP usuwamy
                # wierzchołek v
                s_remove(sp, v)
                # i dodajemy go do SX
                s_add(sx, v)

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

sr = Set()
sp = Set()
sx = Set()
sn = Set()
rm = Set()
# Odczytujemy liczbę
# wierzchołków i krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy puste zbiory
s_create(sr, 0, n-1)
s_create(sp, 0, n-1)
s_create(sx, 0, n-1)
s_create(sn, 0, n-1)
# Pusty zbiór
# kliki największej
s_create(rm, 0, n-1)
# Liczba wierzchołków
# w klice największej
rmsize = 0
# Tworzymy tablicę
# list sąsiedztwa
graf = [None] * n
for i in range(m):
    # Wierzchołki krawędzi
    x = input().split()
    u = int(x[0])
    v = int(x[1])
    # Tworzymy
    # element listy
    p = SLel()
    p.v = u
    # Element dołączamy
    # do listy sąsiadów v
    p.next = graf[v]
    graf[v] = p
    # To samo dla krawędzi
    # w drugą stronę
    p = SLel()
    p.v = v
    # Element dołączamy
    # do listy sąsiadów u
    p.next = graf[u]
    graf[u] = p
# W zbiorze SP
# ustawiamy wszystkie
# wierzchołki
s_full(sp)
print()
# Szukamy rekurencyjnie
# kliki największej
bron_kerbosch(sr, sp, sx)
print("MAXIMUM CLIQUE: ",end="")
s_print(rm)
print()
print("NUMBER OF VERTICES"+
      " IN MAXIMUM CLIQUE =",
      rmsize)
print()
# Usuwamy zbiory
del sr,sp,sx,sn,rm
# Usuwamy tablicę
# list sąsiedztwa
for v in range(n):
    while graf[v]:
        graf[v] = graf[v].next
del graf
Wynik:
8 19
0 1
0 2
0 5
0 6
1 2
1 3
1 5
1 6
2 4
2 5
2 6
2 7
3 4
3 6
3 7
4 6
5 6
5 7
6 7

MAXIMUM CLIQUE: 0 1 2 5 6
NUMBER OF VERTICES IN MAXIMUM CLIQUE = 5
 obrazek

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.