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.
Pojam
Značenje
U primeru
čvor (teme)
objekat koji povezujemo
1, 2, 3, 4, 5
grana (ivica)
veza između dva čvora
1–2, 2–5, …
susedi
čvorovi spojeni granom
susedi čvora 2 su 1, 3 i 5
stepen čvora
broj grana koje izlaze iz čvora
stepen čvora 2 je 3
put
niz čvorova u kome su susedni povezani granom
5 – 2 – 3 – 4
ciklus
put koji se vraća u početni čvor
1 – 2 – 3 – 4 – 1
povezan graf
od svakog čvora postoji put do svakog
da
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;
Predstavljanje
Memorija
Da li su u i v susedi?
Svi susedi čvora u
Kada
matrica susedstva
n²
O(1)
O(n)
n ≤ nekoliko hiljada; Floyd-Warshall
lista susedstva
n + m
O(stepen u)
O(stepen u)
skoro uvek
lista grana
m
O(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)
}
}
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
Uputstvo
Ne treba čuvati ceo graf – dovoljan je niz ulazni[v] koji broji grane koje ulaze u v (ulazni stepen).
Rešenje (C++)
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<int> ulazni(n + 1, 0);
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
ulazni[v]++; // u prati v
}
int naj = 1;
for (int v = 2; v <= n; v++)
if (ulazni[v] > ulazni[naj]) naj = v;
cout << naj << " " << ulazni[naj] << endl;
return 0;
}
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
Uputstvo
Koristi i listu i matricu susedstva: prođi kroz susede čvora a (lista) i za svakog proveri u matrici da li je sused i čvoru b.
Rešenje (C++)
#include <iostream>
#include <vector>
using namespace std;
bool sused[1001][1001];
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> graf(n + 1);
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
graf[u].push_back(v);
graf[v].push_back(u);
sused[u][v] = sused[v][u] = true;
}
int q;
cin >> q;
while (q--) {
int a, b;
cin >> a >> b;
int br = 0;
for (int x : graf[a]) // svaki prijatelj osobe a
if (sused[x][b]) br++; // da li je i prijatelj osobe b?
cout << br << endl;
}
return 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?
Podešavanja kolačića
Koristimo kolačiće da bismo vam pružili najbolje moguće iskustvo. Takođe nam omogućavaju da analiziramo ponašanje korisnika kako bismo stalno poboljšavali veb lokaciju za vas.