Uvod u grafove: pojmovi i predstavljanje

Lekcija 1 od 17 · Grafovi i stabla

Graf je skup čvorova povezanih granama. Gradovi i putevi, prijatelji na društvenoj mreži, polja lavirinta, zadaci koji zavise jedan od drugog – sve su to grafovi. U ovoj lekciji upoznajemo osnovne pojmove i tri načina da se graf zapiše u programu.

Pre nego što počneš: potrebno predznanje – vector u C++, matrice.
Kako da učiš: svaki primer prekucaj i pokreni, a zadatke na kraju prvo reši sam/a; uputstvo i rešenje otvori tek posle toga. Svi programi su u C++-u.

Osnovni pojmovi

    1 ----- 2
    |       |  \
    |       |   5
    4 ----- 3

Ovaj graf ima 5 čvorova i 5 grana: 1–2, 1–4, 2–3, 2–5 i 3–4.

PojamZnačenjeU primeru
čvor (teme)objekat koji povezujemo1, 2, 3, 4, 5
grana (ivica)veza između dva čvora1–2, 2–5, …
susedičvorovi spojeni granomsusedi čvora 2 su 1, 3 i 5
stepen čvorabroj grana koje izlaze iz čvorastepen čvora 2 je 3
putniz čvorova u kome su susedni povezani granom5 – 2 – 3 – 4
ciklusput koji se vraća u početni čvor1 – 2 – 3 – 4 – 1
povezan grafod svakog čvora postoji put do svakogda

Vrste grafova:

  • neusmeren – grana važi u oba smera (prijateljstvo, dvosmerna ulica);
  • usmeren – grana u → v ide samo u jednom smeru (jednosmerna ulica, „zadatak u mora pre zadatka v“);
  • težinski – svaka grana ima broj (dužinu, cenu, vreme).

U zadacima se graf skoro uvek zadaje ovako: u prvom redu broj čvorova n i broj grana m, a zatim m redova sa po dva čvora koje grana spaja (i težinom, ako je graf težinski).

Matrica susedstva

Matrica a dimenzija n × n: a[u][v] = 1 ako postoji grana između u i v, inače 0. Za neusmeren graf matrica je simetrična.

#include <iostream>
using namespace std;

int a[101][101];        // matrica susedstva, čvorovi 1..n (n <= 100)

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        a[u][v] = 1;    // neusmeren graf: grana u oba smera
        a[v][u] = 1;
    }
    // stepen svakog čvora = broj jedinica u njegovoj vrsti
    for (int u = 1; u <= n; u++) {
        int stepen = 0;
        for (int v = 1; v <= n; v++) stepen += a[u][v];
        cout << "stepen(" << u << ") = " << stepen << endl;
    }
    return 0;
}

Provera „da li su u i v povezani granom“ traje O(1), ali matrica zauzima n² memorije – za n = 105 to je 1010 polja, nemoguće.

Lista susedstva

Za svaki čvor čuvamo spisak njegovih suseda – vector<int> graf[n + 1]. Memorija je srazmerna broju grana, pa ovo radi i za milion čvorova. Ovo je predstavljanje koje se koristi u skoro svim algoritmima na grafovima (BFS, DFS, Dijkstra…).

#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> graf(n + 1);   // graf[u] = spisak suseda čvora u
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        graf[u].push_back(v);
        graf[v].push_back(u);          // za usmeren graf ovaj red se izostavlja
    }
    for (int u = 1; u <= n; u++) {
        cout << u << ":";
        for (int v : graf[u]) cout << " " << v;
        cout << endl;
    }
    return 0;
}

Za težinski graf u listu stavljamo parove (sused, težina): vector<vector<pair<int,int>>> graf(n + 1); i graf[u].push_back({v, w});

Lista grana

Ponekad je dovoljno samo zapamtiti sve grane u jedan niz. Tako rade Kruskalov i Bellman-Fordov algoritam:

struct Grana {
    int u, v, w;      // krajevi i težina
};
vector<Grana> grane(m);
for (auto &g : grane) cin >> g.u >> g.v >> g.w;
PredstavljanjeMemorijaDa li su u i v susedi?Svi susedi čvora uKada
matrica susedstvan²O(1)O(n)n ≤ nekoliko hiljada; Floyd-Warshall
lista susedstvan + mO(stepen u)O(stepen u)skoro uvek
lista granamO(m)O(m)Kruskal, Bellman-Ford

Matrica kao graf

Tabla ili lavirint su graf i bez zapisivanja grana: polje (i, j) je čvor, a susedi su mu polja gore, dole, levo i desno. Susede obilazimo pomoću nizova pomeraja:

int di[4] = {-1, 1, 0, 0};
int dj[4] = {0, 0, -1, 1};
for (int k = 0; k < 4; k++) {
    int ni = i + di[k], nj = j + dj[k];
    if (ni >= 0 && ni < n && nj >= 0 && nj < m && tabla[ni][nj] != '#') {
        // (ni, nj) je sused polja (i, j)
    }
}

Tako se rešavaju zadaci Broj ostrva i Najkraći put u lavirintu.

Zadaci za vežbu

Za svaki zadatak postoji primer, uputstvo i rešenje. Rešenja su provereno tačna na primerima iz teksta.

ZADATAK 01Težina: Lako

Najpopularniji korisnik

Na društvenoj mreži sa n korisnika ima m praćenja: red u v znači da korisnik u prati korisnika v (usmerena grana). Ispiši korisnika koga prati najviše ljudi i broj pratilaca; kod jednakog broja – manji redni broj.

Primer

Ulaz

4 5
1 2
3 2
4 2
2 1
4 1

Izlaz

2 3
ZADATAK 02Težina: Srednje

Zajednički prijatelji

Dat je neusmeren graf prijateljstava (n ≤ 1000) i q upita a b. Za svaki upit ispiši koliko zajedničkih prijatelja imaju a i b.

Primer

Ulaz

5 6
1 2
1 3
2 3
2 4
3 4
4 5
3
1 4
2 3
1 5

Izlaz

2
2
0
Česte greške i saveti
  • Čvorovi su u zadacima obično numerisani od 1 – napravi niz veličine n + 1.
  • Kod neusmerenog grafa granu dodaj u oba smera; kod usmerenog samo u → v.
  • Za n veće od nekoliko hiljada ne koristi matricu susedstva – memorija je n².

Proveri znanje

Otvori kviz

Uvod u grafove

1. Šta je stepen čvora u neusmerenom grafu?




2. Koje predstavljanje se koristi za graf sa 10⁵ čvorova i 2·10⁵ grana?




3. Koliko memorije zauzima matrica susedstva grafa sa n čvorova?