Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Prirodno-matematiki
fakultet u Niu
Ivan Stankovi
[STRUKTURE I BAZE
PODATAKA - NELINEARNE
STRUKTURE]
DEFINICIJE I KONCEPTI
STABLA
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).
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.
( +1 1)
(1)
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.
/* 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
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; }
*/
(! 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
*/
*/
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;
}
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.
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>
14
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) +
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.
18
Graf
Matrica susedstva
19
GRAFOVI
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));
}
}
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
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)
#include <stdio.h>
#include <stdlib.h>
#define MAX 10
struct node{
int data;
struct node *link;
};
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);
}
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;
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
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
{0}
nil
{1, 2, 3} 0
{2, 3} 1
{3}
2
{4,5}
3
{5}
4
29
GRAFOVI
{}
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;
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:
R, i elimo da nadjemo minimalno razapinjue stablo za graf G. Dva algoritma koja emo predstaviti u
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;
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
Pri tome:
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
}
}
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
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:
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))
40
41
GRAFOVI
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
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
44
GRAFOVI
45
46
47
GRAFOVI
48
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
3. Napisati metodu koja e vratiti broj vorova stabla koji su roditelji barem jednog lista (tj. koji
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.
50
6.
7.
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.
51
GRAFOVI
11.
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.