Sei sulla pagina 1di 51

2007

Prirodno-matematiki
fakultet u Niu
Ivan Stankovi

[STRUKTURE I BAZE
PODATAKA - NELINEARNE
STRUKTURE]

DEFINICIJE I KONCEPTI

STABLA

1. NELINEARNE STRUKTURE PODATAKA


Najznaajnije nelinearne strukture podataka su stabla i grafovi.

1.1. STABLA
1.1.1. DEFINICIJE I KONCEPTI
Graf G=(V,E) se sastoji od nepraznog skupa vorova G i skupa E koji je skup grana grafa.
Stablo je aciklian, orijentisan graf koji ima jedan vor koji se zove koren (root) sa ulaznim
stepenom 0 dok svi drugi vorovi imaju ulazni stepen 1. Ako izbriemo koren i njegove grane dobijamo
skup disjunktnih stabala. Svaki vor kojiima izlazni stepen 0 naziva se terminalni vor ili list, dok se svi
drugi vorovi nazivaju vorovi grananja (brunch nodes).

Nivo vora je njegova duina od korena, d.

Dubina (visina) stabla je maksimalna vrednost nivoa nekog vora u stablu.

Za stablo se kae da je n-arno (reda n) ako svaki vor ima najvie n podvorova.

Za stablo se kae da je puno ako se svi listovi nalaze na istom rastojanju od korena, tj. ako
od korena do svakog lista odgovara put duine h-1.

Za stablo se kae da je kompletno ako svi njegovi vorovi koji ne predstavljaju listove imaju
svih n odlaznih potega.

Broj vorova kompletnog punog stabla iznosi


C = n0 + n1 + n3 + ... + nh = =0 =

( +1 1)
(1)

Kapacitet vora k predstavlja broj elemenata koji se moe smestiti u vor.

Za stablo se kae da je balansirano ako za svaki vor vai da se broj vorova u svakom
njegovom podstablu ne razlikuje za vie od 1.

OPERACIJE NA BINARNIM STABLIMA

Za stablo reda n iji su svi vorovi na nivoima od 1 do h-1 kompletni, kae se da je


optimalno balansirano.

1.1.2. OPERACIJE NA BINARNIM STABLIMA


Neke od operacija nad binarnim stablom su: prolaz, umetanje, brisanje, pretraivanje i kopiranje.
/* rad sa binarnim stablom - implementacija funkcija koje vrse specificnu
obradu nad cvorovima
binarnog stabla
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct cvor { int broj; struct cvor *levo, *desno; } Cvor;
typedef Cvor *Stablo;
Stablo stvori (void);
stabla.
*/
int vel (Stablo koren);
*/
int zbir (Stablo koren);
*/
void pisi_kld (Stablo koren);
*/
void pisi_lkd (Stablo koren);
*/
void pisi_ldk (Stablo koren);
*/
void crtaj (Stablo koren, int nivo);
*/
int pojav (Stablo koren, int b);
*/
int min_u (Stablo koren);
*/
int max_u (Stablo koren);
*/
int min_n (Stablo koren);
*/
int max_n (Stablo koren);
*/
int uredjeno (Stablo koren);
*/
Cvor *nadji_u (Stablo koren, int b);
*/
Cvor *nadji_n (Stablo koren, int b);
*/
Stablo dodaj_u (Stablo koren, int b);
*/
Stablo dodaj_n (Stablo koren, int b);
*/
Stablo citaj_u (int n);
*/

/* Stavaranje praznog
/* Broj cvorova u stablu.
/* Zbir brojeva u stablu.
/* Prefiksno ispisivanje.
/* Infiksno ispisivanje.
/* Postfiksno ispisivanje.
/* Graficki prikaz stabla.
/* Broj pojavljivanja u stablu.
/* Najmanji u uredjenom stablu.
/* Najveci u uredjenom stablu.
/* Najmanji u neuredjenom stablu.
/* Najveci u neuredjenom stablu.
/* Da li je stablo uredjeno?
/* Trazenje u uredjenom stablu.
/* Trazenje u neuredjenom stablu.
/* Dodavanje u uredjeno stablo.
/* Dodavanje u neuredjeno stablo.
/* Citanje uredjenog stabla.

STABLA

Stablo citaj_n (int n);


/* Citanje neuredjenog stabla.
*/
Stablo brisi (Stablo koren);
/* Brisanje celog stabla.
*/
Stablo izost_u (Stablo koren, int b);
/* Izost. iz uredjenog stabla.
*/
Stablo izost_n (Stablo koren, int b);
/* Izost. iz neuredjenog stabla.
*/
Stablo balans_u (Stablo koren);
/* Balansiranje uredjenog stabla.
*/
Stablo balans_n (Stablo koren);
/* Balansiranje neuredjenog
satbla.*/
int moze (Stablo koren);
/* Da li moze uredjena radnja? */
Stablo radi (Stablo (*f)(Stablo,int), Stablo koren); /* Primena operacije na
stablo za svaki procitani broj */
void main () {
Stablo koren = stvori ();
//stablo
int kraj = 0, broj, n; //indikator kraja rada, element u cvoru stabla,
duzina
char izbor[2];
//izbor korisnika sa menija opcija
//obrada menija opcija koje se prikazuju korisniku
while (!kraj) {
printf ("\nDodavanje brojeva:
a) uredjeno
b) neuredjeno\n"
"Izostavljanje brojeva:
c) uredjeno
d) neuredjeno\n"
"Citanje stabla:
e) uredjeno
f) neuredjeno\n"
"Najmanji element:
g) uredjeno
h) neuredjeno\n"
"Najveci element:
i) uredjeno
j) neuredjeno\n"
"Pretrazivanje:
k) uredjeno
l) neuredjeno\n"
"Balansiranje:
m) uredjeno
n) neuredjeno\n"
"Pisanje stabla:
p) koren-levo-desno\n"
"
q) levo-koren-desno (uredjeno)\n"
"
r) levo-desno-kren\n"
"
s) crtanje\n"
"1. Velicina stabla
2. Zbir elemenata\n"
"3. Broj pojavljivanja
4. Praznjenje stabla\n"
"
0. Zavrsetak rada\n\n"
"Vas izbor? "
);
scanf ("%s", &izbor);
switch (izbor[0]) {
case 'a': case 'A': /* Dodavanje brojeva u uredjeno stablo: */
if (moze (koren)) koren = radi (dodaj_u, koren); break;
case 'b': case 'B': /* Dodavanje brojeva u neuredjeno stablo: */
koren = radi (dodaj_n, koren); break;
case 'c': case 'C': /* Izostavljanje brojeva iz uredjenog stabla: */
if (moze (koren)) koren = radi (izost_u, koren); break;
case 'd': case 'D': /* Izostavljanje brojeva iz neuredjenog stabla: */
koren = radi (izost_n, koren); break;
case 'e': case 'E': /* Citanje uredjenog stabla: */
printf ("Duzina?
"); scanf ("%d", &n);
printf ("Brojevi?
"); koren = brisi (koren); koren = citaj_u (n);
break;
case 'f': case 'F': /* Citanje neuredjenog stabla: */

OPERACIJE NA BINARNIM STABLIMA

printf ("Duzina?
"); scanf ("%d", &n);
printf ("Brojevi?
"); koren = brisi (koren); koren = citaj_n (n);
break;
case 'g': case 'G': case 'h': case 'H':
case 'i': case 'I': case 'j': case 'J':
if (koren) switch (izbor[0]) {
case 'g': case 'G': /* Najmanji element uredjenog stabla: */
if (moze (koren)) printf ("min=
%d\n", min_u (koren)); break;
case 'h': case 'H': /* Najmanji element neuredjenog stabla: */
printf ("min=
%d\n", min_n (koren)); break;
case 'i': case 'I': /* Najveci element uredjenog stabla: */
if (moze (koren)) printf ("max=
%d\n", max_u (koren)); break;
case 'j': case 'J': /* Najveci element neuredjenog stabla: */
printf ("max=
%d\n", max_n (koren)); break;
} else printf ("*** Stablo je parzno! ***\a\n");
break;
case 'k': case 'K': /* Broj pojavljivanja u uredjenom stablu: */
if (moze (koren)) {
printf ("Broj?
"); scanf ("%d", &broj);
printf ("Broj se%s nalazi u stablu.\n",
(nadji_u (koren, broj) != NULL ? "" : " NE"));
} break;
case 'l': case 'L': /* Broj pojavljivanja u neuredjenom stablu: */
printf ("Broj?
"); scanf ("%d", &broj);
printf ("Broj se%s nalazi u stablu.\n",
(nadji_n (koren, broj) != NULL ? "" : " NE"));
break;
case 'm': case 'M': /* Balansiranje uredjenog stabla: */
if (moze (koren)) koren = balans_u (koren); break;
case 'n': case 'N': /* Balansiranje neuredjenog stabla: */
koren = balans_n (koren); break;
case 'p': case 'P': /* Pisanje stabla koren-levo-desno: */
printf ("Stablo=
"); pisi_kld (koren); putchar ('\n'); break;
case 'q': case 'Q': /* Pisanje stabla levo-koren-desno: */
printf ("Stablo=
"); pisi_lkd (koren); putchar ('\n'); break;
case 'r': case 'R': /* Pisanje stabla levo-desno-koren: */
printf ("Stablo=
"); pisi_ldk (koren); putchar ('\n'); break;
case 's': case 'S': /* Crtanje stabla: */
crtaj (koren, 0); break;
case '1':
/* Velicina stabla: */
printf ("Vel=
%d\n", vel (koren)); break;
case '2':
/* Zbir elemenata stabla: */
%d\n", zbir (koren)); break;
printf ("Zbir=
case '3':
/* Broj pojavljivanja datog broja: */
printf ("Broj?
"); scanf ("%d", &broj);
printf ("Broj se pojavljuje %d puta.\n", pojav (koren, broj));
break;
case '4':
/* Praznjenje stabla: */
koren = brisi (koren); break;
case '0':
/* Zavrsetak rada: */
kraj = 1; break;
default: /* Pogresan izbor: */
printf ("*** Nedozvoljeni izbor! ***\a\n"); break;
}
}

STABLA

}
Stablo stvori (void) { return NULL; }

/* Stvaranje praznog stabla.

*/

int vel (Stablo koren)


/* Broj cvorova u stablu.
*/
{ return koren ? 1 + vel (koren->levo) + vel (koren->desno) : 0; }
int zbir (Stablo koren)
/* Zbir brojeva u stablu.
*/
{ return koren ? koren->broj + zbir (koren->levo) + zbir (koren->desno) :
0; }
void pisi_kld (Stablo koren) {
/* Prefiksno ispisivanje.
*/
if (koren) {
printf ("%d ", koren->broj); pisi_kld (koren->levo); pisi_kld (koren>desno);
}
}
void pisi_lkd (Stablo koren) {
/* Infiksno ispisivanje.
*/
if (koren) {
pisi_lkd (koren->levo); printf ("%d ", koren->broj); pisi_lkd (koren>desno);
}
}
void pisi_ldk (Stablo koren) {
/* Postfiksno ispisivanje.
*/
if (koren) {
pisi_ldk (koren->levo); pisi_ldk (koren->desno); printf ("%d ", koren>broj);
}
}
void crtaj (Stablo koren, int nivo) {
/* Graficki prikaz stabla.
*/
if (koren) {
crtaj (koren->desno, nivo+1);
printf ("%*s%d\n", 4*nivo, "", koren->broj);
crtaj (koren->levo, nivo+1);
}
}
int pojav (Stablo koren, int b)
/* Broj pojavljivanja broja b u
stablu.
*/
{ return koren ? (koren->broj==b)+pojav(koren->levo,b)+pojav(koren>desno,b) : 0;}
int min_u (Stablo koren)
/* Najmanji u uredjenom stablu.
*/
{ return koren->levo ? min_u (koren->levo ) : koren->broj; }

OPERACIJE NA BINARNIM STABLIMA


int max_u (Stablo koren)
/* Najveci u uredjenom stablu.
*/
{ return koren->desno ? max_u (koren->desno) : koren->broj; }
int min_n (Stablo koren) {
/* Najmanji u neuredjenom stablu.
*/
int m = koren->broj, k;
if (koren->levo ) { k = min_n (koren->levo ); if (k < m) m = k; }
if (koren->desno) { k = min_n (koren->desno); if (k < m) m = k; }
return m;
}
int max_n (Stablo koren) {
/* Najveci u neuredjenom stablu.
*/
int m = koren->broj, k;
if (koren->levo ) { k = max_n (koren->levo ); if (k > m) m = k; }
if (koren->desno) { k = max_n (koren->desno); if (k > m) m = k; }
return m;
}
int uredjeno (Stablo koren) {
/* Da li je stablo uredjeno?
*/
if (! koren) return 1;
if (koren->levo && (! uredjeno (koren->levo ) ||
max_u (koren->levo) > koren->broj)) return 0;
if (koren->desno && (! uredjeno (koren->desno) ||
min_u (koren->desno) < koren->broj)) return 0;
return 1;
}
Cvor
*/
if
if
if

*nadji_u (Stablo koren, int b) {

/* Trazenje u uredjenom stablu.

(! koren)
return NULL;
(koren->broj == b) return koren;
(koren->broj > b) return nadji_u (koren->levo, b);
return nadji_u (koren->desno, b);

}
Cvor *nadji_n (Stablo koren, int b) {
/* Trazenje u neuredjenom stablu.
*/
if (! koren)
return NULL;
if (koren->broj == b) return koren;
{ Cvor *cvr = nadji_n (koren->levo, b); if (cvr) return cvr; }
return nadji_n (koren->desno, b);
}
Stablo dodaj_u (Stablo koren, int b) {
/* Dodavanje u uredjeno stablo.
*/
if (! koren) {
koren = malloc (sizeof(Cvor));
koren->broj = b; koren->levo = koren->desno = NULL;
} else if (koren->broj > b)
koren->levo = dodaj_u (koren->levo, b);
else if (koren->broj < b)
koren->desno = dodaj_u (koren->desno, b);

STABLA

else if (rand() / (RAND_MAX+1.) < 0.5)


koren->levo = dodaj_u (koren->levo, b);
else
koren->desno = dodaj_u (koren->desno, b);
return koren;
}
Stablo dodaj_n (Stablo koren, int b) {
/* Dodavanje u neuredjeno stablo.
*/
if (! koren) {
koren = malloc (sizeof(Cvor));
koren->broj = b; koren->levo = koren->desno = NULL;
} else if (rand() / (RAND_MAX+1.) < 0.5)
koren->levo = dodaj_u (koren->levo, b);
else
koren->desno = dodaj_u (koren->desno, b);
return koren;
}
Stablo citaj_u (int n) {
/* Citanje uredjenog stabla.
Stablo koren = NULL; int i, b;
for (i=0; i<n; i++) { scanf ("%d", &b); koren = dodaj_u (koren, b); }
return koren;
}

*/

Stablo citaj_n (int n) {


/* Citanje neuredjenog stabla.
Stablo koren = NULL; int i, b;
for (i=0; i<n; i++) { scanf ("%d", &b); koren = dodaj_n (koren, b); }
return koren;
}

*/

Stablo brisi (Stablo koren) {


/* Brisanje celog stabla.
*/
if (koren) {
koren->levo = brisi (koren->levo); koren->desno = brisi (koren->desno);
free (koren); koren = NULL;
}
return koren;
}
Stablo izost_u (Stablo koren, int b) {
/* Izost. iz uredjenog stabla.
*/
if (koren) {
if
(koren->broj > b) koren->levo = izost_u (koren->levo, b);
else if (koren->broj < b) koren->desno = izost_u (koren->desno, b);
else if (koren->levo) {
int m = max_u (koren->levo);
koren->broj = m; koren->levo = izost_u (koren->levo, m);
} else if (koren->desno) {
int m = min_u (koren->desno);
koren->broj = m; koren->desno = izost_u (koren->desno, m);
} else {
free (koren); koren = NULL;
}
}

OPERACIJE NA BINARNIM STABLIMA

10

return koren;
}
Stablo izost_n (Stablo koren, int b) {
/* Izost. iz neuredjenog stabla.
*/
if (koren) {
if (koren->broj == b) {
if
(koren->levo ) {
koren->broj = koren->levo->broj;
koren->levo = izost_n (koren->levo, koren->broj);
} else if (koren->desno) {
koren->broj = koren->desno->broj;
koren->desno = izost_n (koren->desno, koren->broj);
} else { free (koren); koren = NULL; }
} else {
int v = vel (koren->levo); koren->levo = izost_n (koren->levo, b);
if (v == vel (koren->levo)) koren->desno = izost_n (koren->desno, b);
}
}
return koren;
}
Stablo balans_u (Stablo koren) {
/* Balansiranje uredjenog stabla.
*/
if (koren) {
int k = vel (koren->levo) - vel (koren->desno);
for (; k>1; k-=2) {
koren->desno = dodaj_u (koren->desno, koren->broj);
koren->broj = max_u (koren->levo );
koren->levo = izost_u (koren->levo , koren->broj);
}
for (; k<-1; k+=2) {
koren->levo = dodaj_u (koren->levo , koren->broj);
koren->broj = min_u (koren->desno);
koren->desno = izost_u (koren->desno, koren->broj);
}
koren->levo = balans_u (koren->levo );
koren->desno = balans_u (koren->desno);
}
return koren;
}
Stablo balans_n (Stablo koren) {
/* Balansiranje neuredjenog
satbla.*/
if (koren) {
int k = vel (koren->levo) - vel (koren->desno);
for (; k>1; k-=2) {
koren->desno = dodaj_n (koren->desno, koren->broj);
koren->broj = koren->levo ->broj;
koren->levo = izost_n (koren->levo , koren->broj);
}
for (; k<-1; k+=2) {
koren->levo = dodaj_n (koren->levo , koren->broj);
koren->broj = koren->desno->broj;
koren->desno = izost_n (koren->desno, koren->broj);

STABLA

11

}
koren->levo = balans_n (koren->levo );
koren->desno = balans_n (koren->desno);
}
return koren;
}

int moze (Stablo koren) { /* Da li moze uredjena radnja? */


if (! uredjeno (koren)) {
printf ("*** Stablo nije uredjeno! ***\a\n");
return 0;
}
else return 1;
}
/* Primena operacije na stablo za svaki procitani broj: */
Stablo radi (Stablo (*f)(Stablo,int), Stablo koren) {
int b; char zn;
printf ("Brojevi?
");
do { scanf ("%d%c", &b, &zn); koren = (*f) (koren, b); } while (zn !=
'\n');
return koren;
/* do kraja reda */
}
1.

U datoteci zad1in.txt se nalazi niz rei (koje su zapisane u posebnim redovima datoteke) od kojih ni
jedna nije duine vee od 30 karaktera. Ispisati na standardni izlaz samo razliite rei sortirane
leksikografski. Uz svaku re ispisati i broj pojava. Kraj unosa je marker kraja (EOF). Smatrati da je re
niska sastavljen iskljuivo od slova i cifara i broj pojava svake rei nije vei od 10000.

/* Samoreferentne strukture , binarno pretrazivacko stablo


*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define MAXREC 31
typedef struct drvo_tag{
char *rec;
/* pokazuje na rec teksta */
int broj;
/* broj pojavljivanja pod
pretpostavkom da je int dovoljan */
struct drvo_tag *levo;
/* leva grana */
struct drvo_tag *desno;
/* desna grana */
} drvo;
/* Prototipovi funkcija */
drvo *addtree(drvo *, char *);
void treeprint(drvo *);
int uzmi_rec(char *, int, FILE*);
drvo *talloc( void );
char *strdpl(char *);
void osloboditi( drvo *k);
main(){
drvo *koren;
/*koren stabla pretrazivanja */
char rec[MAXREC];
/*sadrzaj reci sa ulaza */
FILE *f;
/*ucitavanje reci ciji broj pojavljivanja se broji */

OPERACIJE NA BINARNIM STABLIMA

12

koren = NULL;
f=fopen("zad1in.txt","r");
while( 1 ){
int kraj = uzmi_rec(rec, MAXREC, f);
/*dodavanje novog cvora u stablo ili izmena nad poljem broj vec
postojeceg cvora */
if( strlen(rec)) koren = addtree( koren, rec);
if(kraj == EOF) break;
/*ucitavanje se vrsi do markera kraja */
}
/*stampanje sadrzaja stabla -leksikografski poredak reci */
treeprint(koren);
/*oslobadjanje zauzetog prostora za stablo pretrage */
osloboditi(koren);
fclose(f);
return 0;
}
/* addtree - dodaje cvor sa tekstom na koji pokazuje w, na ili ispod p u
drvetu*/
drvo *addtree( drvo *p, char *w )
{
int cond;
if( p == NULL )
/* naisla nova rec */
{
p = talloc();
p->rec = strdpl(w);
p->broj = 1;
p->levo = p->desno = NULL;
}
else if ((cond = strcmp(w,p->rec)) == 0 )
p->broj++;
/* ponovljena rec */
else if ( cond < 0 ) /* manje => levi ogranak */
p->levo = addtree(p->levo, w);
else
/*vece =>desni ogranak*/
p->desno = addtree(p->desno, w);
return p;
}
void treeprint(drvo *p) /* treeprint - rekurzivno stampanje drveta*/
{
if( p != NULL )
{
treeprint( p->levo );
printf("%4d %s\n", p->broj, p->rec );
treeprint( p->desno );
}
}
int uzmi_rec(char s[], int lim, FILE *f)
{
char c, i = 0;
/* preskociti sve znake do slova ili cifre */
while(!isalnum(c = s[0] = fgetc(f)) && c!=EOF);
if( c==EOF ) {s[0] = '\0'; return EOF;} /* prazna rec i vratiti EOF */
/* ucitati ostatak reci: (u s[0] se vec nalazi prvo slovo) */
while((c = fgetc(f)) != EOF && isalnum(c) && i < lim)
s[++i] = c;
s[++i] = '\0';
/* zavrsiti nisku */

13

STABLA
if( c==EOF )
return EOF;
return i;

}
/* talloc pravi jedan cvor drveta */
drvo *talloc(void)
{ return (drvo *) malloc(sizeof(drvo)); }
char *strdpl(char *s)
/* pravi se kopija niske s */
{
char *p;
p = (char *) malloc(strlen(s) + 1 );
if( p != NULL ) strcpy(p,s);
return p;
/* u <string.h> postoji standardna funkcija "strdup" koja obavlja navedene
operacije*/
}
void osloboditi( drvo *k)
{ /*rekurzivno se oslobadja levo i desno podstablo korena zadatog stabla */
if (k->levo) osloboditi (k->levo);
if (k->desno) osloboditi (k->desno);
free (k);
/*brisanje cvora koji predstavlja koren zadatog stabla */
}
2.

Napisati program koji sa standardnog ulaza ita aritmetiki izraz zapisan u prefksnoj notaciji operator
izraz1 izraz2, smeta ga u niz karaktera duine do 20 karaktera i formira stablo u ijem se korenu nalazi
zadati operator, u levom podstablu izraz1 a u desnom izraz2. Pri tome se izraz zadaje ili kao ceo broj ili
kao operator izraz1 izraz2. Napisati rekurzivnu funkciju koja od uitanog stringa formira binarno stablo.
Prilikom zadavanja izraza oekujemo da su svi operandi razdvojeni jedan od drugog razmakom i da je
izraz pravilno zadat. Napisati i funkcije koje ovako zadato stablo ispisuju u prefksnom i infiksnom
poretku i funkciju koja rauna vrednost izraza koji se nalazi u stablu.

#include
#include
#include
#include

<stdlib.h>
<stdio.h>
<string.h>
<ctype.h>

/* Definisemo novi tip operacija */


typedef enum operacija {pl = '+',min = '-', pod = '/', put = '*'}
operacija;
/* Struktura koja predstavlja jedan cvor */
typedef struct _cvor{
int ind; /* indikator da li se u cvoru nalazi operator (u tom slucaju
ind ima vrednost 1)
ili broj (u tom slucaju ind ima vrednost 0) */
int br; /* polje u kome cuvamo ceo broj, popunjeno samo ako je polje
ind postavljeno na 0 */
operacija op; /* polje u kome cuvamo operator, popunjeno samo ako je
polje ind postavljeno na 1 */
struct _cvor *l, *d;
} cvor;
/* Funkcija koja pravi jedan cvor pri cemu ostavlja neinicijalizovane
vrednosti br i op */
cvor * napravi_cvor(){
cvor * novi = (cvor *)malloc(sizeof(cvor));
if (novi == NULL){

OPERACIJE NA BINARNIM STABLIMA

14

fprintf(stderr, "greska prilikom alokacije memorije\n");


exit(1);
}
novi->l = NULL;
novi->d = NULL;
return novi;
}
/* Rekurzivna funkcija koja uklanja drvo, obilazak mora biti postorder */
void obrisi_drvo(cvor *drvo){
if (drvo!=NULL){
obrisi_drvo(drvo->l);
obrisi_drvo(drvo->d);
free(drvo);
}
}
/* Rekurzivna funkcija koja parsira string. Predajemo joj adresu
stringa da bi znali dokle smo stigli u toku parsiranja */
void pretvori_u_stablo(char ** s, cvor ** pdrvo){
int c, br;
cvor *novi;
/* Uzimamo sledeci karakter iz stringa. */
c = *(*s);
/* Ako smo stigli do kraja stringa izlazimo */
if (c == '\0') return;
/* U suprotnom popunjavamo kreiramo novi cvor */
novi = napravi_cvor();
/* Ako nije u pitanju cifra znaci da smo ucitali operator... */
if (!isdigit(c)){
/* ...postavljamo indikator na 1 */
novi->ind = 1;
/* ...i operator na ucitan karakter */
novi->op = c;
/* Unosimo podatak u drvo */
*pdrvo = novi;
/* Prelazimo na sledeci relevantan podatak, preskacemo
blanko*/
*s = *s+2;
/* I rekurzivno parsiramo string da bismo formirali levo pa
zatim i desno podstablo */
pretvori_u_stablo(s, &((*pdrvo)->l));
pretvori_u_stablo(s, &((*pdrvo)->d));
}
/* A ako je u pitanju cifra... */
else{
/* ...ucitavamo i ostatak broja ako postoji*/
br = 0;
while(isdigit(c)){
br = br*10+c-'0'; /* uracunavamo tekuci karakter u
broj */
(*s)++; /* pomeramo se za jedno mesto u stringu */
c = *(*s); /* i citamo sledeci karakter */
}
/* postavljamo indikator na 0 */

15

STABLA

novi->ind = 0;
/* i brojevnu vrednost na br */
novi->br = br;
/* Unosimo podatak u drvo */
*pdrvo = novi;
/* Prelazimo na sledeci relevantan podatak. Uzimamo u obzir
da pokazivac vec pokazuje na blanko */
*s = *s+1;
}
}
void ispisi_drvo(cvor *drvo){
if (drvo!=NULL){
/* Prvo ispisujemo koren. Proveravamo vrednost indikatora
*/
if (!drvo->ind)
/* ako je indikator jednak 0 stampamo broj */
printf("%d", drvo->br);
else
/* a inace stampamo karakter */
printf("%c", drvo->op);
/* ...a zatim i levo pa desno podstablo. */
ispisi_drvo(drvo->l);
ispisi_drvo(drvo->d);
}
}
void ispisi_drvo_infiksno(cvor *drvo){
if (drvo == NULL)
return;
if (!drvo->ind)
/* Ako smo naisli na brojevnu vrednost stampamo je. */
printf("%d", drvo->br);
else{
/* U suprotnom imamo pravo stablo pa ispisujemo prvo levu
zagradu... */
printf("(");
/* pa levi izraz... */
ispisi_drvo_infiksno(drvo->l);
/* pa operator... */
printf(" %c ", drvo->op);
/* pa desni izraz... */
ispisi_drvo_infiksno(drvo->d);
/* i na kraju ispisujemo desnu zagradu */
printf(")");
}
}
int izracunaj_drvo(cvor *drvo){
if (!drvo->ind)
return drvo->br;
else
switch (drvo->op){
case '+':
return izracunaj_drvo(drvo->l) +

OPERACIJE NA BINARNIM STABLIMA

16

izracunaj_drvo(drvo->d);
case '-':
return izracunaj_drvo(drvo->l)
izracunaj_drvo(drvo->d);
case '*':
return izracunaj_drvo(drvo->l) *
izracunaj_drvo(drvo->d);
case '/':
return izracunaj_drvo(drvo->l) /
izracunaj_drvo(drvo->d);
}
}
main(){
cvor * drvo = NULL;
char s[20], *ps;
int i=0;
printf("unesite aritmeticki izraz u prefiksnoj notaciji, novi red za
kraj\n");
/* Funkcija fgets cita sa standardnog ulaza (stdin) liniju (karaktere
do unetog novog reda) ili dok se ne popuni 20 karaktera */
if (fgets(s, 20, stdin) == NULL){
fprintf(stderr, "greska");
exit(1);
}
/* Kako s sada sadrzi i oznaku za novi red na kraju brisemo je */
while(s[i]!='\n')
i++;
/* i na kraju dodajemo '\0' da bi pripremili string za prenosenje u
funkciju */
s[i] = s[i+1];
/* Kako je s niz karaktera (a niz je konstantni pokazivac) on ne
sme biti predat funkciji koja kreira stablo (posto funkcija
menja pokazivac koji dobija kao prvi argument) pa uzimamo
pomocni pokazivac da bismo mogli da menjamo njegovu vrednost */
ps = s;
/* Kreiramo stablo pozivom funkcije prevori_u_stablo */
pretvori_u_stablo(&ps, &drvo);
/* Ispisujemo ga u prefiksnom poredku */
ispisi_drvo(drvo);
printf("\n");
/* A zatim i u infiksnom. */
ispisi_drvo_infiksno(drvo);
/* Ispisujemo vrednost pocetnog izraza */
printf(" = %d\n", izracunaj_drvo(drvo));
obrisi_drvo(drvo);
}

GRAFOVI

17

1.2. GRAFOVI
Definicija: Graf G je ureeni par G=(V,E) gde je EVxV. Skup V je skup vorova, dok skup E
predstavlja skup grana (veza izmeu vorova).

Grane usmerenog grafa su ureeni parovi vorova i redosled dva vora koje povezuje
grana je bitan.
Za neusmeren graf vai da ukoliko je vor u u vezi sa vorom v, onda je i vor v u vezi sa
vorom u.
Teinski graf je graf ijim granama su pridrueni jedan ili vie realnih brojeva ( kao
vrednost rastojanja, teina, cene,...).

usmeren graf

neusmeren graf

teinski graf

Stepen d(v) vora v je broj grana susednih voru v (broj grana koje direktno povezuju vor v sa
nekim drugim vorom).

U usmerenom grafu razlikuju se ulazni stepen (broj grana iji kraj je vor v) i izlazni stepen (broj
grana za koje je vor v poetak).

Bipartitivni graf je graf iji se vorovi mogu podeliti na dva disjunktna podskupa tako da u grafu
postoje samo grane izmeu vorova iz razliitih podskupova.

Put od v1 do vk je niz vorova v1, v2, . . . ,vk povezanih granama (v1, v2), (v2, v3), . . . , (vk-1, vk ).

Hamiltonov put je prosti ciklus ili put u kom se svaki vor grafa pojavljuje tano jednom.
NAPOMENA: Sve date definicije su opisne. Za precizne definicije matematikih pojmova vezanih
za grafove konsultovati literaturu.

Predstavljanje grafova u memoriji raunara

18

1.2.1. Predstavljanje grafova u memoriji raunara


Predstavljanje uz pomo matrice susedstva
Jedan nain predstavljanja grafa sa n vorova je uz pomo matrice susedstva dimenzija n x n (svaka
vrsta i kolona odgovaraju po jednom voru grafa). Ukoliko postoji veza izmeu vorova vi i vj tada se u
vrsti koja odgovara voru vi i koloni koja odgovara voru vj je 1 (adj[vi, vj] = 1, ako (vi, vj) ivica u G).
Ukoliko je e ukupan broj ivica grafa, tada e u matrici postojati 2e elemenata matrice koji su jednaki 1,
ako je G neorijentisan graf. Ako je G usmeren graf, samo e elemenata matrice e biti 1. Primeri
predstavljanja grafova uz pomo matrice susedstva su dati na sledeim slikama:

Graf

Matrica susedstva

Ulazni i izlazni stepeni vora

Predstavljanje grafova uz pomo povezanih listi


Drugi nain predstavljanja grafa G je uz pomo povezanih listi. Za svaki vor konsturiemo povezanu
listu koja sadri sve vorove susedne datom voru.

19

GRAFOVI

1.2.2. Raunanje ulaznog i izlaznog stepena vora grafa predstavljenog


pomou matrice susedstva
#include <stdio.h>
#define MAX 10
/* funkcija uz pomoc koje pravimo matricu susedstva */
void buildadjm(int adj[][MAX], int n){
int i,j;
for(i=0;i<n;i++)
for(j=0;j<n;j++){
printf("Enter 1 if there is an edge from %d to %d, otherwise enter 0
\n",i,j);
scanf("%d",&adj[i][j]);
}
}
/* funkcija za racunanje izlaznog stepena cvora */
int outdegree(int adj[][MAX],int x,int n){
int i, count =0;
for(i=0;i<n;i++)
if( adj[x][i] ==1) count++;
return(count);
}
/* funkcija za racunanje ulaznog stepena grafa */
int indegree(int adj[][MAX],int x,int n){
int i, count =0;
for(i=0;i<n;i++)
if( adj[i][x] ==1) count++;
return(count);
}
void main(){
int adj[MAX][MAX],node,n,i;
printf("Enter the number of nodes in graph maximum = %d\n",MAX);

Obilazak grafa u irinu (BFS)

20

scanf("%d",&n);
buildadjm(adj,n);
for(i=0;i<n;i++){
printf("The indegree of the node %d is
%d\n",i,indegree(adj,i,n));
printf("The outdegree of the node %d is %d\n",i,outdegree(adj,i,n));
}
}

1.2.3. Obilazak grafa u irinu (BFS)


BREADTH-FIRST SEARCH je jeadan od najjednostavnijih algoritama za obilazak grafa. Za dati graf
G=(V,E) i startni vor s, BFS obilazi graf G otkrivajui svaki vor koji je dostian iz s. Istovremeno,
moemo raunati i rastojanje vora s do svakog vora do kojeg postoji put iz s. Izvrenjem algoritma
moemo konstruisati tzv. BFS stablo koje e sadrati sve vorove grafa G dostine iz s.

Algoritam se

moe primeniti i na orijentisane i neorijentisane grafove. Na sledeoj slici ilustrovano je izvrenje BFS
algoritma za dati graf G i startni vor s.

BFS(G, s)
for each vertex u V [G] - {s}
do color[u] WHITE
d[u]
[u] NIL
color[s] GRAY
d[s] 0
[s] NIL

21

GRAFOVI

Q
ENQUEUE(Q, s)
while Q
do u DEQUEUE(Q)
for each v Adj[u]
do if color[v] = WHITE
then color[v] GRAY
d[v] d[u] + 1
[v] u
ENQUEUE(Q, v)
color[u] BLACK

1.2.4. Obilazak grafa u dubinu (DFS)


Strategija koju koristi DFS algoritam je traiti vorove sve dublje u grafu kad god je to mogue.
Kod DFS algoritma pretraujemo granu koja naputa vor v i a zatim i sve naredne grane. Kada zavrimo
sa pretragom svih grana koje naputaju vor v vraamo se unazad da bi smo nastavili sa pretragom grana
koje naputaju vor iz kojeg smo stigli u vor v.

DFS(G)
for each vertex u _ V [G]
do color[u] WHITE
[u] NIL
time 0
for each vertex u V [G]
do if color[u] = WHITE
then DFS-VISIT(u)
DFS-VISIT(u)

Obilazak grafa u dubinu (DFS)


color[u] GRAY
White vertex u has just been discovered.
time time +1
d[u] time
for each v _ Adj[u]
Explore edge(u, v).
do if color[v] = WHITE
then [v] u
DFS-VISIT(v)
color[u] BLACK
Blacken u; it is finished.
f [u] time time +1

#include <stdio.h>
#include <stdlib.h>
#define MAX 10
struct node{
int data;
struct node *link;
};

/* funkcija uz pomoc koje pravimo matricu susedstva */


void buildadjm(int adj[][MAX], int n){
int i,j;
for(i=0;i<n;i++)
for(j=0;j<n;j++){
printf("Unesi 1 ako postoji veza izmedju %d i %d, inace unesi 0
\n",i,j);
scanf("%d",&adj[i][j]);
}
}
/* funkcija za racunanje izlaznog stepena cvora */
int outdegree(int adj[][MAX],int x,int n){
int i, count =0;
for(i=0;i<n;i++)
if( adj[x][i] ==1) count++;
return(count);
}
/* funkcija za racunanje ulaznog stepena grafa */
int indegree(int adj[][MAX],int x,int n){
int i, count =0;
for(i=0;i<n;i++)
if( adj[i][x] ==1) count++;
return(count);
}
/* A function to insert a new node in queue*/
struct node *addqueue(struct node *p,int val){
struct node *temp;
if(p == NULL){
p = (struct node *) malloc(sizeof(struct node)); /* insert the
new node first node*/

22

23

GRAFOVI
if(p == NULL){
printf("Rezervisanje memorije nije uspelo\n");
exit(0);
}
p->data = val;
p->link=NULL;
} else {
temp= p;
while(temp->link != NULL){
temp = temp->link;
}
temp->link = (struct node*)malloc(sizeof(struct node));
temp = temp->link;
if(temp == NULL){
printf("Rezervisanje memorije nije uspelo\n");
exit(0);
}
temp->data = val;
temp->link = NULL;
}
return(p);

}
struct node *deleteq(struct node *p,int *val){
struct node *temp;
if(p == NULL){
printf("red je prazan\n");
return(NULL);
}
*val = p->data;
temp = p;
p = p->link;
free(temp);
return(p);
}
void bfs(int adj[][MAX], int x,int visited[], int n, struct node **p){
int y,j,k;
*p = addqueue(*p,x);
do{
*p = deleteq(*p,&y);
if(visited[y] == 0){
printf("\nObilazim cvor = %d\t",y);
visited[y] = 1;
for(j=0;j<n;j++)
if((adj[y][j] ==1) && (visited[j] == 0))
*p = addqueue(*p,j);
}
}while((*p) != NULL);
}
void dfs(int x,int visited[],int adj[][MAX],int n){
int j;
visited[x] = 1;
printf("Posecen je cvor %d\n",x);

Topoloko sortiranje

24

for(j=0;j<n;j++)
if(adj[x][j] ==1 && visited[j] ==0)
dfs(j,visited,adj,n);
}

void main(){
int adj[MAX][MAX],n,i,s;
struct node *start=NULL;
int visited[MAX];
printf("Unesi broj cvorova grafa, maximum = %d\n",MAX);
scanf("%d",&n);
buildadjm(adj,n);
for(i=0;i<n;i++){
printf("Ulazni stepen cvora %d je %d\n",i,indegree(adj,i,n));
printf("Izlazni stepen cvora %d je %d\n",i,outdegree(adj,i,n));
}
while(1){
printf("\n****************** BFS algoritam *****************\n");
printf("Unesi startni cvor: ");
scanf("%d",&s);
if(s==0) break;
for(i=0; i<n; i++)
visited[i] = 0;
bfs(adj,s,visited,n,&start);
}
printf("\n****************** DFS algoritam *****************\n");
for(i=0; i<n; i++)
if(visited[i] ==0)
dfs(i,visited,adj,n);
}

1.2.5. Topoloko sortiranje


Topoloko sortiranje usmerenog aciklinog grafa G=(V,E) predstavlja linearno ureenje svih
vorova tako da ukoliko graf G sadri granu (u,v), tada se vor u nalazi pre vora v u dobijenom nizu
vorova. Ukoliko graf nije aciklian, topoloko sortiranje je nemogue. Usmeren aciklian graf se veoma
esto koristi za predstavljanje veze izmeu dogaaja. Grana (u,v) znai da se dogaaj u mora zavriti pre
nego to pone izvrenje dogaaja v.

GRAFOVI

25

Na slici je dat primer grafa koji predstavlja redosled oblaenja delova odee i topoloko sortiranje
datog grafa (slika (b)).
Algoritam topolokog sortiranje koristi DFS algoritam i moe se prikazati kao:
TOPOLOGICAL-SORT(G)
call DFS(G) to compute finishing times f[v] for each vertex v
as each vertex is finished, insert it onto the front of a linked list
return the linked list of vertices
3.

Napisati program koji e topoloki sortirati graf koji je predstavljen uz pomo povezanih listi.

#include <stdio.h>
#include <stdlib.h>
#ifndef MAX_NAME_CHAR
#define MAX_NAME_CHAR 50
#endif
typedef enum {FALSE, TRUE} bool;
typedef struct node node;
struct node {
int count;

// za cvorove grafa : ulazni stepen


// za cvorove u listi : cvor sa kojim je cvor grafa

povezan
node *next;
};
node *graph;
node *zerolist;
void addToZerolist(int v){
/*
* dodajemo cvor v u zerolist ukoliko v ima ulazni stepen 0
*/
node *ptr = (node *)malloc( sizeof(node) );

Topoloko sortiranje
ptr->count = v;
ptr->next = zerolist;
zerolist = ptr;
}
node *buildGraph(char *fileName, int *n) {
/* ucitavamo graf iz datoteke
pretpostavimo da u prvom redu datoteke imamo date brojeve m n
m - broj cvorova
n - broj grana
a u ostalih n redova sve grane grafa u obliku u v, gde su u i v
cvorovi */
int i,edges,u,v;
FILE *f;
f=fopen(fileName,"r");
fscanf(f,"%d %d",n,&edges);
// inicijalizujemo graf
graph = (node *)malloc((*n)*sizeof(node));
for(i=0;i<(*n);i++) {
graph[i].count = 0;
graph[i].next = NULL;
}
// now add the list entries.
for( i=0; i<edges; ++i ) {
// dodajemo novi cvor u graf
node *ptr = (node *)malloc( sizeof(node) );
fscanf(f,"%d - %d",&u,&v);
ptr->count = v;
ptr->next = graph[u].next;
graph[u].next = ptr;
// increase indegree of dst.
graph[v].count++;
}
// kreiramo listu cvorova sa ulaznim stepenom 0
zerolist = NULL;
for(i=0;i<(*n);i++)
if(graph[i].count == 0 ) {
addToZerolist(i);
}
}
void printGraph(node *graph, int n) {
int i;
node *ptr;
for(i=0;i<n;i++){
node *ptr;
printf( "%d: pred=%d: ",i,graph[i].count);
for(ptr=graph[i].next; ptr; ptr=ptr->next )
printf( "%d ", ptr->count );
printf( "\n" );
}
printf( "zerolist: " );
for( ptr=zerolist; ptr; ptr=ptr->next )

26

27

GRAFOVI
printf( "%d ", ptr->count );
printf( "\n" );

}
int getZeroVertex() {
/*
* vraca cvor sa ulaznim stepenom 0.
* ukoliko takav cvor ne postoji vraca -1.
*/
int v;
node *ptr;
if( zerolist == NULL )
return -1;
ptr = zerolist;
v = ptr->count;
zerolist = zerolist->next;
free(ptr);
return v;
}
void removeVertex( int v ) {
/*
* brise cvor v i sve njegove odlazne grane iz grafa
*/
node *ptr;
graph[v].count = -1;
// oslobadjamo listu graph[v].next.
for( ptr=graph[v].next; ptr; ptr=ptr->next ) {
if( graph[ ptr->count ].count > 0 ) //
graph[ ptr->count ].count--;
if( graph[ ptr->count ].count == 0 )
addToZerolist( ptr->count );
}
}
void topsort( int nvert ) {
/*
* rekurzivna funkcija koja topoloski sortira graf
*/
int v;
if( nvert > 0 ) {
v = getZeroVertex();
if( v == -1 ) {
fprintf( stderr, "graph contains a cycle.\n" );
return;
}
printf( " -> %d", v );
removeVertex(v);
topsort( nvert-1 );
}
}

Topoloko sortiranje

28

int main() {
int n;
buildGraph("mat.txt",&n);
printGraph(graph,n);
topsort(n);
}

Objanjenje
Digraf G je predstavljen uz pomo povezanih listi. U ovakvoj reprezentaciji G je niz
povezana lista vorova grafa sa kojima je

graph[0n1], gde je svaki element graph[i]


vor i povezan, a n je broj vorova u grafu G.

Promenljiva zerolist slui da bismo uvali listu vorova koji nemaju prethodnike.
Algoritam topsort() se slui rekurzijom. Iz promenljive zerolist, uklanjamo vektor v
koji ima 0 prethodnika i tampa ga. Ovaj vor v ili nema prethodnika u grafu G, ili su svi
njegovi prethodnici ve obieni. Svi vorovi u zerolist su potencijalni kandidati za sledei
vor koji e biti odtampan, tj. ubaen u sortirani niz. Nakon to smo odtampali v svi
vorovi sa kojima je on u vezi mogu postati kandidati za sledei odtampani vor.
Primer: Analiza algoritma na sledeem grafu

Korak Zerolist Izlaz


0
1
2
3
4
5

{0}
nil
{1, 2, 3} 0
{2, 3} 1
{3}
2
{4,5}
3
{5}
4

29

GRAFOVI

Korak Zerolist Izlaz


6

{}

1.2.6. Jako povezane komponente grafa


Povezana komponenta grafa je maksimalni podgraf dataog grafa koji je povezan. Razmotrimo
sledei graf.

Povezana komponenta ovog grafa je:

Jako povezane komponente


Za digraf G, jako povezana komponenta je podgraf grafa G u kojem za svaki par vorova (u,v) vai
da postoji put i od u do v i od vora v do vora u. Posmatrajmo graf na sledeoj slici.

Jako povezane komponente grafa

Jako povezane komponente datog grafa su:

30

31

GRAFOVI

#include <stdio.h>
#include <stdlib.h>
#define MAXVERTICES 20
#define MAXEDGES 20
typedef enum {FALSE, TRUE, TRISTATE} bool;
typedef struct node node;
struct node {
int dst;
node *next;
};
void printGraph( node *graph[], int nvert ) {
/*
* prints the graph.
*/
int i;
for( i=0; i<nvert; ++i ) {
node *ptr;
for( ptr=graph[i]; ptr; ptr=ptr->next )
printf( "[%d] ", ptr->dst );
printf( "\n" );
}
}
void insertEdge( node **ptr, int dst ) {
/*
* insert a new node at the start.
*/
node *newnode = (node *)malloc( sizeof(node) );
newnode->dst = dst;
newnode->next = *ptr;
*ptr = newnode;
}
void buildGraph( node *graph[], int edges[2][MAXEDGES], int nedges ) {
/*
* fills graph as adjacency list from array edges.
*/
int i;
for( i=0; i<nedges; ++i ) {
insertEdge( graph+edges[0][i], edges[1][i] );
insertEdge( graph+edges[1][i], edges[0][i] ); // undirected graph.
}
}
void dfs( int v, int *visited, node *graph[] ) {
/*
* recursively traverse graph from v using visited.
* and mark all the vertices that come in dfs path to TRISTATE.
*/
node *ptr;

Jako povezane komponente grafa

32

visited[v] = TRISTATE;
//printf( "%d \n", v );
for( ptr=graph[v]; ptr; ptr=ptr->next )
if( visited[ ptr->dst ] == FALSE )
dfs( ptr->dst, visited, graph );
}
void printSetTristate( int *visited, int nvert ) {
/*
* prints all vertices of visited which are TRISTATE.
* and set them to TRUE.
*/
int i;
for( i=0; i<nvert; ++i )
if( visited[i] == TRISTATE ) {
printf( "%d ", i );
visited[i] = TRUE;
}
printf( "\n\n" );
}
void compINC(node *graph[], int nvert) {
/*
* prints all connected components of graph represented using INC lists.
*/
int *visited;
int i;
visited = (int *)malloc( nvert*sizeof(int) );
for( i=0; i<nvert; ++i )
visited[i] = FALSE;
for( i=0; i<nvert; ++i )
if( visited[i] == FALSE ) {
dfs( i, visited, graph );
// print all vertices which are TRISTATE.
// and mark them to TRUE.
printSetTristate( visited, nvert );
}
free( visited );
}

int main() {
FILE *f;
int edges[2][MAXEDGES],i,nvert,nedges;
node **graph;
f=fopen("mat.txt","r");
fscanf(f,"%d %d",&nvert,&nedges);
for(i=0;i<nedges;i++)
fscanf(f,"%d %d",&edges[0][i],&edges[1][i]);
graph = (node **)calloc(nvert, sizeof(node *) );
buildGraph( graph, edges, nedges );

33

GRAFOVI
printGraph( graph, nvert );
compINC( graph, nvert );
fclose(f);
return 0;

Graf je predstavljen uz pomo povezanih listi. Graf je predstavljen kao niz od n pokazivaa gde n
predstavlja broj vorova grafa. Svaki lan niza i sadri pokaziva na povezanu listu vorova sa kojima je
vor i u vezi. Na primer, sledei graf:

se predstavlja sledeom strukturom:

1.2.7. Minimum Spanning Tree (minimalno razapinjue stablo)


Definicija: Neka je dat povezan, neorijentisani graf G = (V, E) sa teinskom funkcijom w : E R.
Minimalno razapinjue stablo grafa G je podgraf (V,T), gde je T E, koji povezuje sve vorove grafa G i

ija je ukupna teina (,) (, ) minimalna.

Pretpostavimo da imamo povezan, neorijentisani graf G = (V, E) sa teinskom funkcijom w : E

R, i elimo da nadjemo minimalno razapinjue stablo za graf G. Dva algoritma koja emo predstaviti u

Minimum Spanning Tree (minimalno razapinjue stablo)

34

osnovi imaju istu strategiju nalaenja minimalnog razapinjueg stabla. Naime, oba polaze od skupa grana
A koji je na poetku prazan, a u svakom koraku dodaju tom skupu po jednu granu, koju nazivamo sigurna
grana za podgraf A (safe edge), sve dok ne dobijemo minimalno razapinjue stablo (znai imamo n-1
koraka). U svakom koraku A je podskup minimalnog razapinjueg stabla.
GENERIC-MST(G, w)
A
while A does not form a spanning tree
do find an edge (u, v) that is safe for A
A A {(u, v)}
return A

Kruskal-ov algoritam

MST-KRUSKAL(G, w)
A
for each vertex v V[G]
do MAKE-SET(v)
sort the edges of E into nondecreasing order by weight w
for each edge (u, v) E, taken in nondecreasing order by weight
do if FIND-SET(u) FIND-SET(v)
then A A _ {(u, v)}
UNION(u, v)
return A
U toku generisanja minimalnog drveta razapinjanja, sve vreme se manipulie sa supergrafom koji je
indukovan polaznim grafom. Ako je polazni graf G=(V,E), onda je supergraf ureeni par SG=(SV,SE), takav
da je:

SV skup vorova;

SE skup superivica;

svaki supervor sv je podskup skupa vorova V;

svaka dva supervora su disjunktna;

unija svih supervorova je skup V;

izmeu supervorova su i sv postoji ivica ako i samo ako postoji vor u su i vor v sv,
tako da je (u,v)E.

duina superivice (su,sv) je duina najkrae ivice grafa (G,E) iji je jedan kraj u supervoru
su, a drugi u supervoru su.

35

GRAFOVI

Na poetku izvrenja Kruskalovog algoritma supervorovi su jednolani, tj. svakom voru u


polaznog grafa odgovara supervor su={u}, a E je prazan. Algoritam je iterativan. U svakoj iteraciji SE
izvrava sledei niz radnji:

dva supervora se spajaju u jedan, ime se broj supervorova smanjuje;

jedna ivica se dodaje minimalnom drvetu (skupu E).

Pri tome:

spajaju se najblii supervorovi (supervorovi izmeu kojih je najmanje rastojanje);

odreuje se najkraa ivica izmeu vorova koji su sadrani u paru prethodno odreenih
supervorova i ta ivica se dodaje drvetu.

Petlja se ponavlja dok ne dobijemo supergraf sa samo jednim supervorom. Kako je na poetku
ukupno |V| supervorova, izvrie se ukupno |V|-1 iteracija.
#include <stdio.h>
#include <stdlib.h>
#define MAXV 20
#define MAXEDGES 190
void buildGraph(int adj[][MAXV], int nver, int edges[3][MAXEDGES], int nedges
) {
/*
* popunjavamo matricu susedstva
*/
int i,j;
for(i=0;i<nver;i++)
for(j=0;j<nver;j++)
adj[i][j]=0;
for( i=0; i<nedges; ++i ) {
adj[edges[0][i]][edges[1][i]]=edges[2][i];
adj[edges[1][i]][edges[0][i]]=edges[2][i];//neorijentisani graf
}
}

int mdr_kruskal(int vn, int e[][MAXV], int v1[], int v2[]){


/**************************************
vn - broj cvorova u polaznom grafu
e - matrica duzina ivica
v1 - niz za zapis prvog kraja ivica drveta
v2 - niz za zapis drugog kraja ivica drveta
rezultat:
0 - polazni graf je povezan, odredjeno drvo razapinjanja
-1 - polazni graf nije povezan, drvo nije odredjeno
****************************************/
int i,j,k,l1,l2;

Minimum Spanning Tree (minimalno razapinjue stablo)

36

int sv[MAXV];
for(i=0;i<vn;i++) sv[i]=i;
for(i=0;i<vn-1;i++){
for(j=0;j<vn;j++){
for(k=0;k<vn;k++)
if((sv[j]!=sv[k]) && (e[j][k]!= 0)) break;
if(k<vn) break;
}
if(j==vn) return -1;
l1=j;l2=k;
for(;j<vn;j++)
for(k=0;k<vn;k++)
if((sv[j]!=sv[k]) && (e[j][k]!=0) &&
(e[j][k]<e[l1][l2])){
l1=j;
l2=k;
}
v1[i]=l1;v2[i]=l2;
j=min(sv[l1],sv[l2]);
k=max(sv[l1],sv[l2]);
l1=j;l2=k;
for(j=0;j<vn;j++)
if(sv[j]==l2) sv[j]=l1;
}
return 0;
}
int main() {
FILE *f;
int
edges[3][MAXEDGES],i,nvert,nedges,adj[MAXV][MAXV],v1[MAXV],v2[MAXV];
f=fopen("mat.txt","r");
fscanf(f,"%d %d",&nvert,&nedges);
for(i=0;i<nedges;i++)
fscanf(f,"%d %d %d",&edges[0][i],&edges[1][i],&edges[2][i]);
fclose(f);
buildGraph(adj,nvert,edges,nedges);
if(mdr_kruskal(nvert,adj,v1,v2)==-1){
printf("\nPolazni graf nije povezan. Nemoguce naci drvo
razapinjanja.");
return 1;
}
printf("\nPronadjeno minimalno drvo razapinjanja:");
for(i=0;i<nvert-1;i++)
printf("\n%d -> %d",v1[i],v2[i]);
printf("\n*************** kraj ****************\n");
return 0;
}

Isti algoritam se moe modifikovati tako da postane efikasniji. Naime, u svakom prolazu kroz
spoljanju petlju se odreuje najkraa ivica iji su krajevi u razliitim supervorovima. Ako bi se ivice
sortirale pre poetka izvravanja te petlje, onda se raunanje pojednostavljuje. Tada bismo u petlji
pronalazili sledeu ivicu iji su krajevi u razliitim supervorovima, i dodavali bismo je drvetu.

37

GRAFOVI

Minimum Spanning Tree (minimalno razapinjue stablo)

38

Prim-ov algoritam
MST-PRIM(G, w, r)
for each u _ V [G]
do key[u]
[u] NIL
key[r] 0
Q V [G]
while Q
do u EXTRACT-MIN(Q)
for each v Adj[u]
do if v Q and w(u, v) < key[v]
then [v] u
key[v] w(u, v)

Kod Primovog algoritma drvo se generie tako to se kree od proizvoljnog vora koji e biti koren

drveta, i dodaje jedan po jedan vor. Tako da u toku izvrenja algoritma imamo:

skup U koji se sastoji od vorova ve dodatih drvetu i

skup V\U koji se sastoji od vorova koji nisu u drvetu

Na poetku se skup U sastoji samo od jednog vora izabrani koren drveta. U svakoj iteraciji se bira
nakraa ivica iji je jedan kraj (vor u) u skupu U, a drugi (vor v) u skupu V\U. vor v se dodaje skupu U,
a ivica (u,v) se dodaje drvetu (E).

39

GRAFOVI

Izbor najkre ivice sa zadanim svojstvima je ekvivalentan izboru vora iz skupa V\U koji je najblii
skupu vorova U. Rastojanje izmeu vora i skupa vorova se definie kao duina najkrae ivice iji je
jedan kraj upravo taj vor, a drugi kraj je u skupu.
Da bismo smanjili raunanje, nakon dodavanja novog vora (u) u skup U, za sve vorove iz skupa
V\U korigujemo najkrae rastojanje do skupa U. Korekciju izvodimo po formuli:
d(v,U{u}) = min {d(v,U),e(u,v)},
gde je e(u,v) duina ivice (u,v).
Postupak se ponavlja sve dok skup U ne postane jednak skupu V. Kako se u svakom prolazu u skup
U dodaje jedan vor, izvrava se ukupno |V|-1 iteracija. Funkcija se moe zapisati na sledei nain:
(glavni program je identian kao kod kruskalovig algoritma, sem poziva funkcije mdr_Prim umesto
mdr_kruskal)
int mdr_Prim(int vn, int e[][MAXV],int v1[], int v2[]){
int i,j,k;
int vi[MAXV];
int vp[MAXV];
int dm[MAXV];
/*** vi[i] ima vrednost 1, ako je i vec ukljucen u drvo, 0 u suprotnom
***/
vi[0] = 1;
for(i=1;i<vn;i++){
vi[i]=0;
dm[i]=0;
}
for(i=1;i<vn;i++)
if(e[0][i] != 0){
/***
dm[i] je rastojanje cvora i od drveta,
vp[i] je cvor iz drveta koji je najblizi cvoru i, tj. vazi
dm[i]=e[vp[i]][i]
***/
vp[i]=0;
dm[i]=e[0][i];
}
for(i=0;i<vn-1;i++){
j=0;
while((j<vn)&&(vi[j] || (dm[j] == 0))) j++;
if(j==vn) return -1;
for(k=j++;j<vn;j++)
if(!vi[j] && (dm[j] != 0) && (dm[k] > dm[j])) k=j;
vi[k]=1;
v1[i]=vp[k];
v2[i]=k;
for(j=0;j<vn;j++)
if(!vi[j] && (e[k][j] != 0))

Minimum Spanning Tree (minimalno razapinjue stablo)


if((dm[j] == 0) || (dm[j] > e[k][j])){
vp[j]=k;
dm[j]=e[k][j];
}
}
return 0;
}

40

41

GRAFOVI

1.2.8. Nalaenje najkraeg puta od jednog do svih ostalih vorova


INITIALIZE-SINGLE-SOURCE(G, s)
1 for each vertex v _ V[G]
2 do d[v]
3 [v] NIL
4 d[s] 0
RELAX(u, v, w)
1 if d[v] > d[u] + w(u, v)
2 then d[v] d[u] + w(u, v)
3 [v] u

Bellman Ford algoritam


Bellman-Ford algoritam nalazi najkrae rastojanje od datog vora do svih ostlih vorova grafa ak i

za sluaj kada postoje ivice sa negativnom duinom. Algoritam otkriva postojanje negativnih petlji i u
tom sluaju daje odgovarajui izvetaj, odnosno ne odreuje rastojanja.
Polazna ideja je da svaki put, pa i najkrai izmeu bilo koja dva vora, moe imati najvie |V| -1
ivicu. Tako se izvrava |V| -1 prolaz kroz petlju u kojoj se proverava za svaku ivicu da li smanjuje najkrae
rastojanje izmeu polaznog vora i nekog od preostalih vorova.
Nakon toga jo jednom ponovimo navedeni postupak za svaku ivicu. Ako se tim prolazom skrati
neko od izraunatih rastojanja, u grafu postoji petlja negativne duine do koje se moe stii od polaznog
vora (s) i odreivanje najkraih rastojanja nema smisla.
BELLMAN-FORD(G, w, s)
INITIALIZE-SINGLE-SOURCE(G, s)
for i 1 to |V[G]| - 1
do for each edge (u, v) _ E[G]
do RELAX(u, v, w)
for each edge (u, v) _ E[G]
do if d[v] > d[u] + w(u, v)
then return FALSE
return TRUE

Na slici je prikazan graf i rezultat izvrenja ovog algoritma.

Nalaenje najkraeg puta od jednog do svih ostalih vorova

42

#include <stdio.h>
#include <stdlib.h>
#define MAXV 20
#define MAXEDGES 190
#define NEPOZNATO 1000000
void buildGraph(int adj[][MAXV], int nver, int edges[3][MAXEDGES], int nedges
) {
/*
* popunjavamo matricu susedstva
*/
int i,j;
for(i=0;i<nver;i++)
for(j=0;j<nver;j++)
adj[i][j]=0;
for( i=0; i<nedges; ++i ) {
adj[edges[0][i]][edges[1][i]]=edges[2][i];//graf je orijentisan
}
}

43

GRAFOVI

int Ford_Belman(int vn, int vs, int e[][MAXV], int d[]){


/*****
ulaz:
vn - broj cvorova u grafu
vs - polazni cvor
e - duzine ivica
izlaz:
d - duzine najkracih puteva od vs do ostalih cvorova
rezultat:
0 - nije odredjeno rastojanje zato sto se od vs moze stici do
ciklusa negativne duzine
1 - odredjena su najkrca rastojanja i upisana su u niz d
******/
int i,j,k;
for(i=0;i<vn;i++)
if(e[vs][i] != 0)
d[i]=e[vs][i];
else
d[i]= NEPOZNATO;
for(i=1;i<vn;i++)
for(j=0;j<vn;j++)
for(k=0;k<vn;k++)
if(e[j][k] != 0)
if(d[j] != NEPOZNATO)
if((d[k]==NEPOZNATO) ||
(d[j]+e[j][k]<d[k]))
d[k]=d[j]+e[j][k];
for(j=0;j<vn;j++)
for(k=0;k<vn;k++)
if(e[j][k] != 0)
if((d[j] != NEPOZNATO) && (d[k] != NEPOZNATO) &&
(d[j]+e[j][k]<d[k]))
return 0;
return 1;
}
int main() {
FILE *f;
int edges[3][MAXEDGES],i,nvert,nedges,adj[MAXV][MAXV],d[MAXV],vs;
f=fopen("graph.txt","r");
fscanf(f,"%d %d",&nvert,&nedges);
for(i=0;i<nedges;i++)
fscanf(f,"%d %d %d",&edges[0][i],&edges[1][i],&edges[2][i]);
fclose(f);
buildGraph(adj,nvert,edges,nedges);
printf("\n************* ALGORITAM BELLMAN-FORD ******************\n");
printf("\nUnesi startni cvor (0-%d):",nvert-1);
scanf("%d",&vs);
if(!Ford_Belman(nvert,vs,adj,d)){
printf("\nPolazni graf sadrzi cikl negativne duzine. Nemoguce
odrediti rastojanja!");
return 1;
}
for(i=0;i<nvert;i++)

Nalaenje najkraeg puta od jednog do svih ostalih vorova

44

printf("\nRastojanje od cvora %d do cvora %d iznosi:


%d",vs,i,d[i]);
printf("\n*************** kraj ****************\n");
return 0;
}

Dijkstrin algoritam za nalaenje najkraih puteva


U svakoj iteraciji ovog algoritma se odreuje najkrae rastojanje od poetnog vora (s) do jednog
od preostalih vorova. U toku izvravanja algoritma manipulie se skupom vorova U za koje je
izraunato najkrae rastojanje od vora s.
Na poetku taj skup se sastoji samo od jednog vora (U={s}). U svakoj iteraciji skupu U se dodaje
jedan vor. Pored toga, za svaki od vorova vV\U odreuje se najkrai put (duina najkraeg puta) od
vora s do vora v, pri emu svi vorovi na tom putu osim vora v moraju biti u skupu U, naravno, ako
takav put postoji. Skupu U se dodaje vor iz skupa V\U kome je taj put najkrai.
Duine puteva se odreuju na poetku (kada je U={s}), posle ega se koriguje pri dodavanju novog
elementa u skup U. Na poetku je duina puta jednaka:
duini ivice izmeu vora s i odgovarajueg vora ako ivica postoji;
+, tj. put ne postoji, ako ivica od vora s do tog vora ne postoji.
Po dodavanju novog vora (u) u skup U, raunati put se moe promeniti. Jasno, taj put se menja
samo ako se na njemu nalazi i vor u. Novi put vodi od vora s do vora u, a nakon toga du ivice od
vora u do vora v (ako postoji ivica (u,v)). Tako novi put ima duinu jednaku zbiru duine puta od vora s
do u i duinu ivice od vora u do vora v (ako ta ivica postoji). Najkrai put je jednak minimumu
dosadanje duine puta i gore pomenutog zbira.
#include <stdio.h>
#include <stdlib.h>
#define MAXV 20
#define MAXEDGES 190
#define NEPOZNATO 1000000
void buildGraph(int adj[][MAXV], int nver, int edges[3][MAXEDGES], int nedges
) {
/*
* popunjavamo matricu susedstva
*/
int i,j;
for(i=0;i<nver;i++)
for(j=0;j<nver;j++)
adj[i][j]=0;

GRAFOVI

45

for( i=0; i<nedges; ++i ) {


adj[edges[0][i]][edges[1][i]]=edges[2][i];//graf je orijentisan
}
}
void dijkstra(int vn, int vs, int e[][MAXV], int d[]){
int i,j,vm;
int id[MAXV];
for(i=0;i<vn;i++)
d[i]=e[vs][i]?e[vs][i]:NEPOZNATO;
d[vs]=0;
for(i=0;i<vn;i++) id[i]=0;
id[vs]=1;
for(i=0;i<vn;i++){
j=0;
while((j<vn) && (id[j] || (d[j] == NEPOZNATO))) j++;
if(j>=vn) break;
vm=j;
while(j<vn){
if(!id[j] && (d[j]!= NEPOZNATO) && (d[j]<d[vm])) vm=j;
j++;
}
id[vm]=1;
for(j=0;j<vn;j++)
if(!id[j] && (e[vm][j] != 0))
if((d[j] == NEPOZNATO) || (d[vm]+e[vm][j]<d[j]))
d[j]=d[vm]+e[vm][j];
}
}
int main() {
FILE *f;
int edges[3][MAXEDGES],i,nvert,nedges,adj[MAXV][MAXV],d[MAXV],vs;
f=fopen("graph.txt","r");
fscanf(f,"%d %d",&nvert,&nedges);
for(i=0;i<nedges;i++)
fscanf(f,"%d %d %d",&edges[0][i],&edges[1][i],&edges[2][i]);
fclose(f);
buildGraph(adj,nvert,edges,nedges);
printf("\n************* ALGORITAM DIJKSTRA ********************\n");
printf("\nUnesi startni cvor (0-%d):",nvert-1);
scanf("%d",&vs);
dijkstra(nvert,vs,adj,d);
for(i=0;i<nvert;i++)
printf("\nRastojanje od cvora %d do cvora %d iznosi:
%d",vs,i,d[i]);
printf("\n*************** kraj ****************\n");
return 0;
}

Najkrae rastojanje izmeu svih parova vorova (Floyd-Warshall)

46

1.2.9. Najkrae rastojanje izmeu svih parova vorova (Floyd-Warshall)


Alogoritam Floyd-Warshall a podrazumeva iterativni postupak za odreivanje najkraih rastojanja
izmeu svih parova vorova.
Na poetku vorove numeriemo brojevima od 0 do n-1. Neka je d(k)i,j najkrai put izmeu vorova i
i j takav da su svi vorovi na tom putu, osim krajnjih, manji ili jednaki voru k. Jasno, tada je d(-1)i,j jednako
duini ivice izmeu vorova i i j, ako postoji. Takoe je d(n-1)i,j jednako upravo najkraem putu izmeu
vorova i i j.
Raunanje vrednosti d(k+1)i,j je prilino jednostavno. Moe se pokazati da je:

47

GRAFOVI

d(k+1)i,j = min(d(k)i,j , d(k)i,k+1 + d(k)k+1,j)


Program za raunanje najkraih rastojanja izgleda ovako:
#include <stdio.h>
#include <stdlib.h>
#define MAXV 20
#define MAXEDGES 190
#define NEPOZNATO 1000000
void buildGraph(int adj[][MAXV], int nver, int edges[3][MAXEDGES], int nedges
) {
/*
* popunjavamo matricu susedstva
*/
int i,j;
for(i=0;i<nver;i++)
for(j=0;j<nver;j++)
adj[i][j]=0;
for( i=0; i<nedges; ++i ) {
adj[edges[0][i]][edges[1][i]]=edges[2][i];//graf je orijentisan
}
}
void floyd_warshall(int vn, int ve[][MAXV], int me[][MAXV]){
int vi, vj, vk;
for(vi=0;vi<vn;vi++)
for(vj=0;vj<vn;vj++)
if(vi==vj)
me[vi][vj]=0;
else if(ve[vi][vj] != 0)
me[vi][vj] = ve[vi][vj];
else
me[vi][vj] = NEPOZNATO;
for(vk=0;vk<vn;vk++)
for(vi=0;vi<vn;vi++){
for(vj=0;vj<vn;vj++){
if((me[vi][vk] != NEPOZNATO) && (me[vk][vj] != NEPOZNATO))
if((me[vi][vj] == NEPOZNATO) ||(me[vi][vk]+me[vk][vj] < me[vi][vj]))
me[vi][vj] = me[vi][vk] + me[vk][vj];
}
}
}
int main() {
FILE *f;
int
edges[3][MAXEDGES],i,j,nvert,nedges,adj[MAXV][MAXV],shortest[MAXV][MAXV];
f=fopen("graph.txt","r");
fscanf(f,"%d %d",&nvert,&nedges);
for(i=0;i<nedges;i++)
fscanf(f,"%d %d %d",&edges[0][i],&edges[1][i],&edges[2][i]);
fclose(f);
buildGraph(adj,nvert,edges,nedges);

Najkrae rastojanje izmeu svih parova vorova (Floyd-Warshall)

48

printf("\n******** ALGORITAM FLOYD - WARSHALL *****************\n");


floyd_warshall(nvert,adj,shortest);
for(i=0;i<nvert;i++)
for(j=0;j<nvert;j++)
printf("\nRastojanje od cvora %d do cvora %d iznosi: %d",i,j,shortest[i][j]);
printf("\n*************** kraj ****************\n");
return 0;
}

49

GRAFOVI

2. Zadaci za vebu
1. Napisati program koji vorove binarnog stabla tampa u redosledu koji odgovara
nivoima (prvo element koji se nalazi u prvom nivou, pa elemente drugog nivoa i tako
redom). Za ispis elemenata stabla po nivoima koristiti red.
/* Struktura reda u kojoj cemo cuvati pokazivace na elemente stabla */
typedef struct red{
cvor *el;
struct red * sl;
} red;
/* Struktura binarnog stabla*/
typedef struct cvor { int broj; struct cvor *levo, *desno; } Cvor;
typedef Cvor *Stablo;
2. Napisati metodu koja ce vratiti broj cvorova binarnog stabla kod kojih je zbir elemenata levog

podstabla vei od zbira elemenata desnog podstabla.


/* Struktura binarnog stabla*/
typedef struct cvor { int broj; struct cvor *levo, *desno; } Cvor;
typedef Cvor *Stablo;

3. Napisati metodu koja e vratiti broj vorova stabla koji su roditelji barem jednog lista (tj. koji

imaju barem jedan list kao direktnog potomka).


/* Struktura binarnog stabla*/
typedef struct cvor { int broj; struct cvor *levo, *desno; } Cvor;
typedef Cvor *Stablo;

4. Napisati metodu koja vraa labelu onog lista binarnog stabla koji ima najmanji nivo. Ukoliko

postoji vie listova na najmanjem nivou, treba vratiti onaj sa najmanjom labelom. Koren stabla
se nalazi na nivou 0.
/* Struktura binarnog stabla*/
typedef struct cvor { char c; struct cvor *levo, *desno; } Cvor;
typedef Cvor *Stablo;

5.

Dva binarna stabla su slina kao u ogledalu ako su oba prazna ili ako nisu prazna, ako
je levo stablo svakog stabla slino kao u ogledalu desnom stablu onog drugog. Na
sledeoj slici su prikazana dva slina kao u ogledalu stabla:

Napiite funkciju koja e proveriti da li su dva binarna stabla slina kao u ogledalu.

Najkrae rastojanje izmeu svih parova vorova (Floyd-Warshall)

50

/* Struktura binarnog stabla*/


typedef struct cvor { int broj; struct cvor *levo, *desno; } Cvor;
typedef Cvor *Stablo;

6.

Rekonstruiite binarno stablo iji vorovi imaju imena A, B, . . ., J ako je poznato da


INORDER obilazak ispisuje vorove u redosledu GDJEAICHBF, a POSTORDER obilazak u
redosledu DJGIABHFCE. Detaljno obrazloite svaki korak. Nakon rekonstrukcije ispiite i
PREORDER obilazak dobijenog stabla. Nacrtajte jo neko stablo koje ima iste PREORDER i
POSTORDER obilaske kao ovo.
/* Struktura binarnog stabla*/
typedef struct cvor { int broj; struct cvor *levo, *desno; } Cvor;
typedef Cvor *Stablo;

7.

Kaemo da je vor p binarnog stabla zajedniki predak vorova


u i v ako je p istovremeno predak vora u i predak vora v.
vor np je najblii predak od u i v ako od svih njihovih
zajednikih predaka upravo np ima najveu visinu. Napiite
funkciju node np(BTREE T, node u, node v) koja vraa ime
najblieg pretka vorova u i v. U primeru desno, vorovi a i c su
zajedniki pretci vorova f i e - ali kako c ima veu visinu, on je
najblii predak.

8.

Udaljenost vorova u i v raunamo kao broj grana koji treba prei na putu koji spaja u i v.
Napiite funkciju int udalj(BTREE T, node u, node v) koja vraa
udaljenost vorova u i v u stablu T. Moete koristiti pozive funkcije iz (a). U primeru
desno, duina puta koji spaja f i e je jednaka 3.

9.

Kaemo da je vor v binarnog stabla T k-potomak vora


u ako se v nalazi u podstablu od T kojem je u koren i pri
tome je nivo vora v za k vei od nivoa vora u. Tako su
npr. deca nekog vora njegovi 1-potomci, deca od dece 2potomci, itd. Napiite funkciju sa prototipom
labeltype potomak(BTREE T, int k) koja
vraa label onog vora bin. stabla T koji ima najvie kpotomaka. Ako ima vie takvih vorova, vratite labelu bilo
kojeg. Funkcija treba biti neovisna o implementaciji atp-a
BTREE; ne smete koristiti pomone atp-ove. Moete
definisati pomone funkcije i globalne varijable. U primeru desno, uzmimo da je k = 2.
vor a ima dvoje 2-potomaka, a vor c jednog; kako ostali vorovi nemaju 2-potomaka,
funkcija treba vratiti a.

51

GRAFOVI

10.Napiite funkciju sa prototipom labeltype

najvisi_list(BTREE T) koja vraa


labelu onog lista binarnog stabla T koji ima najmanji nivo. Ako ima vie listova na
najmanjem nivou, treba vratiti onaj sa najmanjim label. U primeru desno, listovi su b, e i
f, najmanji nivo od njih ima b i taj nivo je 1; dakle funkcija za ovaj primer treba vratiti b.

11.

Rekonstruiite binarno stablo iji vorovi imaju imena A, B, . . ., J ako je poznato da


INORDER obilazak ispisuje vorove u redosledu GEAFDBHICJ, a POSTORDER obilazak u
redosledu EFAGIHJCBD. Detaljno obrazloite svaki korak. Nakon rekonstrukcije ispiite i
PREORDER obilazak dobivenog stabla.

12.

Date su dve datoteke koje u svakoj liniji sadre po jednu nisku sa ne vie od 80
karaktera. Napisati program koji na standardni izlaz ispisuje sve niske prve datoteke
koje nisu sadrane u drugoj datoteci. Zadatak realizovati korienjem binarnog stabla
pretrage.