106
1 1. UVODNI ZADACI 1.1. Pokazivači – osnovni pojmovi 1. Promenljive i pokazivači #include <stdio.h> main(){ int x = 3; /* Adresu promenjive x zapamticemo u novoj promeljivoj. Nova promenljiva je tipa pokazivaca na int (int*) */ int* px; printf("Adresa promenljive x je : %p\n", &x); printf("Vrednost promenljive x je : %d\n", x); px = &x; printf("Vrednost promenljive px je (tj. px) : %p\n", px); printf("Vrednost promenljive na koju ukazuje px (tj. *px) je : %d\n", *px); /* Menjamo vrednost promenljive na koju ukazuje px */ *px = 6; printf("Vrednost promenljive na koju ukazuje px (tj. *px) je : %d\n", *px); /* Posto px sadrzi adresu promenljive x, ona ukazuje na x tako da je posredno promenjena i vrednost promenljive x */ printf("Vrednost promenljive x je : %d\n", x); } 2. Popuniti sledeću tabelu: A B C P1 P2 Init 1 2 3 &A &C *P1 = (*P2)++ 3 2 4 &A &C P1 = P2 3 2 4 &C &C P1 = &B 3 2 4 &B &C *P1 = *P1 - *P2 3 -2 4 &B &C

1. UVODNI ZADACInasport.pmf.ni.ac.rs/materijali/2805/Vezbe SPA.pdf1 1. #0B 1. UVODNI ZADACI 1.1. Pokazivači – osnovni pojmovi 1. Promenljive i pokazivači #include

  • Upload
    others

  • View
    11

  • Download
    0

Embed Size (px)

Citation preview

1

1. # 0B

1. UVODNI ZADACI

1.1. Pokazivači – osnovni pojmovi

1. Promenljive i pokazivači #include <stdio.h> main(){ int x = 3; /* Adresu promenjive x zapamticemo u novoj promeljivoj. Nova promenljiva je tipa pokazivaca na int (int*) */ int* px; printf("Adresa promenljive x je : %p\n", &x);

printf("Vrednost promenljive x je : %d\n", x); px = &x; printf("Vrednost promenljive px je (tj. px) : %p\n", px); printf("Vrednost promenljive na koju ukazuje px (tj. *px) je : %d\n", *px); /* Menjamo vrednost promenljive na koju ukazuje px */ *px = 6; printf("Vrednost promenljive na koju ukazuje px (tj. *px) je : %d\n", *px); /* Posto px sadrzi adresu promenljive x, ona ukazuje na x tako da je posredno promenjena i vrednost promenljive x */ printf("Vrednost promenljive x je : %d\n", x); }

2. Popuniti sledeću tabelu:

A B C P1 P2

Init 1 2 3 &A &C

*P1 = (*P2)++ 3 2 4 &A &C P1 = P2 3 2 4 &C &C P1 = &B 3 2 4 &B &C

*P1 = *P1 - *P2 3 -2 4 &B &C

2

3. Swap : Demonstracija prenosa argumenata preko pokazivaca

#include <stdio.h> /* Pogresna verzija funkcije swap. Zbog prenosa po vrednosti, funkcija razmenjuje kopije promenljivih iz main-a, a ne samih promenljivih */ void swap_wrong(int x, int y){ int tmp; printf("swap_wrong: "); printf("Funkcija menja vrednosti promenljivim na adresama : \n"); printf("x : %p\n", &x); printf("y : %p\n", &y); tmp = x; x = y; y = tmp; } /* Resenje je prenos argumenata preko pokazivaca */ void swap(int* px, int* py){ int tmp; printf("swap : Funkcija menja vrednosti promenljivim na adresama : \n"); printf("px = %p\n", px); printf("py = %p\n", py); tmp = *px; *px = *py; *py = tmp; } int main(){ int x = 3, y = 5; printf("Adresa promenljive x je %p\n", &x); printf("Vrednost promenljive x je %d\n", x); printf("Adresa promenljive y je %p\n", &y); printf("Vrednost promenljive y je %d\n", y); /* Pokusavamo zamenu koristeci pogresnu verziju funkcije */ swap_wrong(x, y); printf("Posle swap_wrong:\n"); printf("Vrednost promenljive x je %d\n", x); printf("Vrednost promenljive y je %d\n", y); /* Vrsimo ispravnu zamenu. Funkciji swap saljemo adrese promenljvih x i y, a ne njihove vrednosti */ swap(&x, &y); printf("Posle swap:\n"); printf("Vrednost promenljive x je %d\n", x); printf("Vrednost promenljive y je %d\n", y); return 0;

3

}

1.2. Pokazivači – veze sa nizovima

4. Pokazivačka aritmetika

#include <stdio.h> main(){ char s[] = "abcde"; int t[] = { 1, 2, 3, 4, 5 }; /* Inicijalizujmo pokazivace ps i pt na pocetke nizova s i t */ char* ps = &s[0]; int* pt = &t[0]; /* Pokazivace je moguce sabirati sa celim brojevima i od njih je moguce oduzimati cele brojeve*/ /* Ispisimo vrednosti pokazivaca */ printf("ps = %p\n", ps); printf("ps+1 = %p\n", ps + 1); printf("ps+2 = %p\n", ps + 2); printf("ps-1 = %p\n", ps - 1); printf("ps-2 = %p\n", ps - 2); /* Prilikom sabiranja pokazivaca i celih brojeva, dodaje se velicina odgovarajuceg tipa. */ printf("pt = %p\n", pt); printf("pt+1 = %p\n", pt + 1); printf("pt+2 = %p\n", pt + 2); printf("pt-1 = %p\n", pt - 1); printf("pt-2 = %p\n", pt - 2); /* Na pokazivace je moguce primenjivati i operatore ++ i -- */ for (ps = s; *ps; ps++) putchar(*ps); putchar('\n'); /* Slicno, dva pokazivaca istog tipa se mogu oduzimati. Prilikom sracunavanja rezultata, uzima se u obzir velicina tipa. */ ps = &s[3]; printf("s = %p\n", s); printf("ps = %p\n", ps); printf("ps - s = %d\n", ps - s); pt = &t[3]; printf("t = %p\n", t); printf("pt = %p\n", pt); printf("pt - t = %d\n", pt - t); }

4

5. Veza između pokazivača i nizova

#include <stdio.h> void print_array(int* pa, int n); int main(){ int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int num_of_elements = sizeof(a) / sizeof(int); int* pa; /* Niz je isto sto i adresa prvog elementa */ printf("Niz a : %p\n", a); printf("Adresa prvog elementa niza a (&a[0]) : %p\n", &a[0]); /* Moguce je dodeliti niz pokazivacu odgovarajuceg tipa */ pa = a; printf("Pokazivac pa ukazuje na adresu : %p\n", pa); /* Nizu nije moguce dodeliti pokazivacku promenljivu (nizove mozemo smatrati KONSTANTNIM pokazivacima na prvi element) */ /* a = pa; */ /* Niz je moguce koristiti kao pokazivac tj. vaze pravila pokazivacke aritmetike*/ printf("a + 3 = %p\n", a + 3); /* Vazi da je a + i = &a[i] odnosno *(a + i) = a[i] */ printf("&a[3] = %p\n", &a[3]); /* Identiteti a + i = &a[i] odnosno *(a + i) = a[i] vazi i za pokazivace i za nizove */ /* Pokazivace je na osnovu prethodnog moguce indeksirati kao nizove */ printf("pa[5] = %d\n", pa[5]); printf("*(pa + 5) = %d\n", *(pa + 5)); /* Medjutim, sizeof(pa) je samo velicina pokazivaca, a ne niza */ printf("sizeof(a) = %d\n", sizeof(a)); printf("sizeof(pa) = %d\n", sizeof(pa)); /* Pozivamo funkciju za stampanje niza i saljemo joj niz */ print_array(a, num_of_elements); /* Pozivamo funkciju za stampanje niza i saljemo joj pokazivac na pocetak niza */ print_array(pa, num_of_elements); } /* Proslednjivanje niza u funkciju void print_array(int pa[], int n);

5

je ekvivalentno prosledjivanju pokazivaca u funkciju void print_array(int* pa, int n); Izmedju ovih konstrukcija nema nikakve razlike. */ void print_array(int* pa, int n){ int i; for (i = 0; i < n; i++) printf("%d ", pa[i]); putchar('\n'); }

6. Implementiranje funkcija iz biblioteke string.h - strlen, strcpy, strcat, strcmp, strchr, strstr, ... - verzije sa pokazivacima

#include <stdio.h> #include <stdlib.h> /* Zbog NULL */ /* Izracunava duzinu stringa */ int string_length(char* s) { char* t; for (t = s; *t; t++); return t - s; } /* Kopira string src u string dest. Pretpostavlja da u dest ima dovoljno prostora.*/ void string_copy(char *dest, char *src) { /* Kopira karakter po karakter, sve dok nije iskopiran karakter ’\0’ */ while (*dest++ = *src++); } /* Nadovezuje string t na kraj stringa s. Pretpostavlja da u s ima dovoljno prostor */ void string_concatenate(char *s, char *t) { /*Pronalazimo kraj stringa s*/ while (*s) s++; /*Vrsi se kopiranje, slicno kao u funkciji string_copy*/ while (*s++ = *t++); } /* Vrsi leksikografsko poredjenje dva stringa. Vraca : 0 - ukoliko su stringovi jednaki <0 - ukoliko je s leksikografski ispred t >0 - ukoliko je s leksikografski iza t */ int string_compare(char* s, char* t) { /* Petlja ide sve dok ne naidjemo na prvi razlicit karakter */ for (; *s == *t; s++, t++) if (*s == '\0') /* Naisli smo na kraj oba stringa, a nismo nasli razliku */ return 0;

6

/* *s i *t su prvi karakteri u kojima se niske razlikuju. Na osnovu njihovog odnosa, odredjuje se odnos stringova */ return *s - *t; } /* Pronalazi prvu poziciju karaktera c u stringu s, i vraca pokazivac na nju, odnosno NULL ukoliko s ne sadrzi c */ char* string_char(char* s, char c) { for (; *s; s++) if (*s == c) return s; /* Nije nadjeno */ return NULL; } /* Pronalazi poslednju poziciju karaktera c u stringu s, i vraca pokazivac na nju, odnosno NULL ukoliko s ne sadrzi c */ char* string_last_char(char* s, char c) { char* t = s; /* Pronalazimo kraj stringa s */ while (*t++); /* Krecemo od kraja i trazimo c unazad */ for (t--; t >= s; t--) if (*t == c) return t; /* Nije nadjeno */ return NULL; } /* Proverava da li string str sadrzi string sub. Vraca poziciju na kojoj sub pocinje, odnosno -1 ukoliko ga nema */ char* string_string(char* str, char* sub) { char* s, * t; /* Proveravamo da li sub pocinje na svakoj poziciji i */ for (; *str; str++) /* Poredimo sub sa str pocevsi od poziciji i sve dok ne naidjemo na razliku for (s = str, t = sub; *s == *t; s++, t++) /* Nismo naisli na razliku a ispitali smo sve karaktere niske sub */ if (*(t + 1) == '\0') return str; /* Nije nadjeno */ return NULL; } int main(){ char s[100]; char t[] = "Zdravo"; char u[] = " svima"; char r[] = "racunari"; string_copy(s, t); printf("%s\n", s); string_concatenate(s, u); printf("%s\n", s); printf("%d\n", string_char(r, 'n') - r);

7

printf("%d\n", string_last_char(r, 'a') - r); printf("%d\n", string_string(r, "racun") - r); printf("%d\n", string_string(r, "ari") - r); printf("%d\n", string_string(r, "cun") - r); printf("%p\n", string_string(r, "cna")); return 0; }

8

2. LINEARNE STRUKTURE PODATAKA

2.1. 1BPOLJE

2.1.1. 5BDEFINICIJE I STRUKTURA

Polje (array) predstavlja linearnu homogenu statičku strukturu podataka i sastoji se od fiksnog

broja komponenata istog tipa. Svaki element polja se može označiti pomoću indeksa i svakom se

elementu može direktno pristupati. Ukupan broj komponenti polja određen je pri njegovom opisu i ne

može se menjati. Polje može biti jednodimenzionalno i višedimenzionalno.

Adresa i-te komponente vektora A je data jednačinom:

LR0R+c*(i-1),

gde su: LR0R – adresa prve reči prvog elementa vektora A, a c – broj reči pridružen svakom

elementu.

Dvodimenzionalna polja se mogu memorisati po vrstama ili kolonama i mogu se prikazati

ekvivalentnim jednodimenzionalnim vektorom. Adresa elementa A[i,j] dvodimenzionalnog polja

smeštenog po kolonama data je izrazom:

LR0R+c*[(j-1)*n + (i-1)],

gde je n broj vrsta.

Analogno, adresa elementa A[i,j] dvodimenzionalnog polja smeštenog po vrstama data je

izrazom:

LR0R+c*[(i-1)*n + (j-1)].

Analogno se smeštaju i višedimenzionalna polja u memoriju.

7. NCP koji sa standardnog ulaza učitava raspored 8 topova na šahovskoj tabli. Raspored se učitava u formi 8 linija sa po 8 brojeva po liniji. Ako na datom polju nema topa, učitava se 0, a inače 1. Program mora da ispita validnost unetog rasporeda ( da li su učitani brojevi ili 0 ili 1, da li ima 8 topova) i ispita da li se u datom rasporedu dva topa tuku.

#include <stdio.h> #include <stdlib.h> int main() { int tabla[8][8]; /*matrica nula i jedinica cuva raspored 8 topova na tabli */ int suma; /* pamti se suma reda/kolone kao test osobina iz zahteva zadatka */ int i, j; /*brojaci u petljama */ /*ucitavanje podataka o rasporedu topova uz sumiranje broja topova i u slucaju greske stampa se odgovarajuca poruka i okoncava programa */ suma = 0; for (i = 0; i < 8; i++)

9

for (j = 0; j < 8; j++) { scanf("%d", &tabla[i][j]); /*test korektnosti ulaza */ if (tabla[i][j] != 0 && tabla[i][j] != 1) { printf("\nNekorektni ulazni podaci o rasporedu topova\n"); exit(EXIT_FAILURE); } suma = suma + tabla[i][j]; } /*greska je ako je broj topova na tabli razlicit od 8 */ if (suma != 8) { printf("\nNekorektni ulazni podaci o broju topova\n"); exit(EXIT_FAILURE); } /*proveravanje da li se dva topa nalaze u istom redu , tj. da li je suma clanova nekog reda veca od 1 */ for (i = 0; i < 8; i++) { suma = 0; for (j = 0; j < 8; j++) suma += tabla[i][j]; if (suma > 1) { printf("\nTopovi se tuku\n"); exit(EXIT_SUCCESS); } } /*proveravanje da li se dva topa nalaze u istoj koloni , tj. da li je suma clanova neke kolone veca od 1 */ for (j = 0; j < 8; j++) { suma = 0; for (i = 0; i < 8; i++) suma += tabla[i][j]; if (suma > 1) { printf("\nTopovi se tuku\n"); exit(EXIT_SUCCESS); } } /*inace se topovi ne tuku */ printf("\nTopovi se ne tuku\n"); exit(EXIT_SUCCESS); }

8. Ubacivanje elementa u rastuce sortiran niz

#pragma warning(disable: 4996) #include <stdio.h> #include <stdlib.h> void umetniUNiz(int value, int niz[], int n) { int i = n-1; while (niz[i] > value && i >= 0) { niz[i + 1] = niz[i];

10

i--; } niz[i + 1] = value; } int main() { FILE* f; int niz[100], n, i, trenDuz, value; f = fopen("zad07.txt", "r"); fscanf(f, "%d", &n); trenDuz = 0; for (i = 0; i < n; i++) { fscanf(f, "%d", &value); umetniUNiz(value, niz, trenDuz); trenDuz++; } fclose(f); printf("\nUcitana duzina niza je: %d\n", n); printf("Ucitani niz je:\n"); for (i = 0; i < n; i++) { printf("%d ", niz[i]); } printf("\n"); system("PAUSE"); return 0;

}

9. Metoda int prosekParnih(int [] niz) koja računa i vraća prosek parnih elemenata niza (onih koji imaju parne vrednosti, a ne na parnim mestima u nizu).

#pragma warning(disable: 4996) #include <stdio.h> #include <stdlib.h> double prosekParnihElemenata(int niz[], int n) { double pv = 0.0; int i, be = 0, zbir = 0; for (i = 0; i < n; i++) { if (niz[i] % 2 == 0) { zbir += niz[i]; be++; } } if (be > 0) { pv = (double)zbir / be; } return pv; } int main() {

11

FILE* f; int i, n, niz[100]; double pv; f = fopen("zad08.txt", "r"); fscanf(f, "%d", &n); for (i = 0; i < n; i++) { fscanf(f, "%d", &niz[i]); } fclose(f); pv = prosekParnihElemenata(niz, n); printf("Prosecna vrednost parnih elemenata niza je: %.2f\n", pv); system("PAUSE"); return 0;

}

2. 2. 4B

12

2.3. LANČANE LISTE

2.3.1. 8B Definicija

Lančana lista, za razliku od prethodnih struktura koristi lančani (spregnuti) način zauzeća

memorije. Prednosti korišćenja lančane liste:

- Količina rezervisane memorije ne mora biti unapred zadata i fiksirana. Tačan iznos zauzete

memorije će zavisiti isključivo od količine podataka koji se obrađuju.

- Moguće je brzo i često brisanje i umetanje podataka.

Elementi lančane liste nisu memorisani u uzastopnim memorijskim lokacijama, već svaki element

eksplicitno ukazuje na naredni element u memoriji. Postoji nekoliko tipova listi:

1) jednostruko spregnuta lista

2) dvostruko spregnuta lista

3) kružna lista

2.3.2. 9BJEDNOSTRUKO SPREGNUTA LINEARNA LISTA

Svaki element ove strukture sadrži dva polja: podatak i pokazivač na naredni element u listi.

Glava liste je pokazivač koji sadrži adresu prvog elementa liste.

/* Rad sa jednostruko spregnutom listom - iterativne verzije funkcija za rad sa

povezanom listom */

#include <stdio.h>

#include <stdlib.h>

typedef struct elem {

int broj;

struct elem *sled;

} Elem; /*Element liste*/

int duz (Elem *lst); /* Broj elemenata liste. */

void pisi (Elem *lst); /* Ispisivanje liste. */

Elem *na_pocetak (Elem *lst, int b); /* Dodavanje na pocetak. */

Elem *na_kraj (Elem *lst, int b); /* Dodavanje na kraj. */

Elem *citaj1 (int n); /* Citanje liste stavljajuci brojeve na pocetak. */

Elem *citaj2 (int n); /* Citanje liste stavljajuci brojeve na kraj. */

Elem *umetni (Elem *lst, int b); /* Umetanje u uredjenu listu. */

void brisi (Elem *lst); /* Brisanje svih elemenata liste. */

Elem *izostavi (Elem *lst, int b); /* Izostavljanje svakog pojavljivanja. */

void main () {

Elem *lst = NULL; int kraj = 0, izbor, broj, n;

while (!kraj) {

printf ("\n1. Dodavanje broja na pocetak liste\n"

"2. Dodavanje broja na kraj liste\n"

"3. Umetanje broja u uredjenu listu\n"

"4. Izostavljanje broja iz liste\n"

"5. Brisanje svih elemenata liste\n"

"6. Citanje uz obrtanje redosleda brojeva\n"

13

"7. Citanje uz cuvanje redosleda brojeva\n"

"8. Odredjivanje duzine liste\n"

"9. Ispisivanje liste\n"

"0. Zavrsetak rada\n\n"

"Vas izbor? "

);

scanf ("%d", &izbor);

switch (izbor) {

case 1: case 2: case 3: case 4:

printf ("Broj? "); scanf ("%d", &broj);

switch (izbor) {

case 1: /* Dodavanje broja na pocetak liste: */

lst = na_pocetak (lst, broj); break;

case 2: /* Dodavanje broja na kraj liste: */

lst = na_kraj (lst, broj); break;

case 3: /* Umetanje broja u uredjenu listu: */

lst = umetni (lst, broj); break;

case 4: /* Izostavljanje broja iz liste: */

lst = izostavi (lst, broj); break;

}

break;

case 5: /* Brisanje svih elemenata liste: */

brisi (lst); lst = NULL; break;

case 6: case 7: /* Citanje liste: */

printf ("Duzina? "); scanf ("%d", &n);

printf ("Elementi? "); brisi (lst);

switch (izbor) {

case 6: /* uz obrtanje redosleda brojeva: */

lst = citaj1 (n); break;

case 7: /* uz cuvanje redosleda brojeva: */

lst = citaj2 (n); break;

}

break;

case 8: /* Odredjivanje duzine liste: */

printf ("Duzina= %d\n", duz (lst)); break;

case 9: /* Ispisivanje liste: */

printf ("Lista= "); pisi (lst); putchar ('\n'); break;

case 0: /* Zavrsetak rada: */

kraj = 1; break;

default: /* Pogresan izbor: */

printf ("*** Neozvoljeni izbor! ***\a\n"); break;

}

}

}

/* Definicije funkcija za obradu lista (iterativno). */

int duz (Elem *lst) { /* Broj elemenata liste. */

int n = 0;

while (lst){

n++;

lst = lst->sled;

} return n;

}

void pisi (Elem *lst){ /* Ispisivanje liste. */

while(lst){

printf("%d ",lst->broj);

lst = lst -> sled;

}

}

Elem *na_pocetak(Elem *lst, int b){ /* Dodavanje na pocetak. */

Elem *novi = malloc(sizeof(Elem));

14

novi->broj = b; novi->sled = lst;

return novi;

}

Elem *na_kraj (Elem *lst, int b) { /* Dodavanje na kraj. */

Elem *novi = malloc (sizeof(Elem));

novi->broj = b; novi->sled = NULL;

if (!lst) return novi;

else {

Elem *tek = lst;

while (tek->sled) tek = tek->sled;

tek->sled = novi;

return lst;

}

}

Elem *citaj1 (int n) { /* Citanje liste stavljajuci brojeve na pocetak. */

Elem *prvi = NULL; int i;

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

Elem *novi = malloc (sizeof(Elem));

scanf("%d", &novi->broj);

novi->sled = prvi;

prvi = novi;

}

return prvi;

}

Elem *citaj2 (int n) { /* Citanje liste stavljajuci brojeve na kraj. */

Elem *prvi = NULL, *posl = NULL; int i;

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

Elem *novi = malloc (sizeof(Elem));

scanf ("%d", &novi->broj); novi->sled = NULL;

if (!prvi)

prvi = novi;

else posl->sled = novi;

posl = novi;

}

return prvi;

}

Elem *umetni (Elem *lst, int b) { /* Umetanje u uredjenu listu. */

Elem *tek = lst, *pret = NULL, *novi;

while(tek && tek->broj < b){

pret = tek;

tek = tek->sled;

}

novi = malloc (sizeof(Elem));

novi->broj = b;

novi->sled = tek;

if(!pret)

lst = novi;

else pret->sled = novi;

return lst;

}

void brisi (Elem *lst) { /* Brisanje svih elemenata liste. */

while(lst){

Elem *stari = lst;

lst = lst->sled;

free (stari);

}

}

Elem *izostavi(Elem *lst, int b){ /* Izostavljanje svakog pojavljivanja. */

Elem *tek = lst, *pret = NULL;

15

while(tek)

if (tek->broj != b){

pret = tek;

tek = tek->sled;

}

else {

Elem *stari = tek;

tek = tek->sled;

if (!pret) lst = tek; else pret->sled = tek;

free (stari);

}

return lst;

}

/* Rad sa povezanom listom - REKURZIVNE verzije

funkcija za rad sa povezanom listom;

*/

#include <stdio.h>

#include <stdlib.h>

typedef struct elem { int broj; struct elem *sled; } Elem; /*Element liste*/

int duz (Elem *lst); /* Broj elemenata liste. */

void pisi (Elem *lst); /* Ispisivanje liste. */

Elem *na_pocetak (Elem *lst, int b); /* Dodavanje na pocetak. */

Elem *na_kraj (Elem *lst, int b); /* Dodavanje na kraj. */

Elem *citaj1 (int n); /* Citanje liste stavljajuci brojeve na pocetak. */

Elem *citaj2 (int n); /* Citanje liste stavljajuci brojeve na kraj. */

Elem *umetni (Elem *lst, int b); /* Umetanje u uredjenu listu. */

void brisi (Elem *lst); /* Brisanje svih elemenata liste. */

Elem *izostavi (Elem *lst, int b); /* Izostavljanje svakog pojavljivanja. */

void main () {

Elem *lst = NULL; int kraj = 0, izbor, broj, n;

while (!kraj) {

printf ("\n1. Dodavanje broja na pocetak liste\n"

"2. Dodavanje broja na kraj liste\n"

"3. Umetanje broja u uredjenu listu\n"

16

"4. Izostavljanje broja iz liste\n"

"5. Brisanje svih elemenata liste\n"

"6. Citanje uz obrtanje redosleda brojeva\n"

"7. Citanje uz cuvanje redosleda brojeva\n"

"8. Odredjivanje duzine liste\n"

"9. Ispisivanje liste\n"

"0. Zavrsetak rada\n\n"

"Vas izbor? "

);

scanf ("%d", &izbor);

switch (izbor) {

case 1: case 2: case 3: case 4:

printf ("Broj? "); scanf ("%d", &broj);

switch (izbor) {

case 1: /* Dodavanje broja na pocetak liste: */

lst = na_pocetak (lst, broj); break;

case 2: /* Dodavanje broja na kraj liste: */

lst = na_kraj (lst, broj); break;

case 3: /* Umetanje broja u uredjenu listu: */

lst = umetni (lst, broj); break;

case 4: /* Izostavljanje broja iz liste: */

lst = izostavi (lst, broj); break;

}

break;

case 5: /* Brisanje svih elemenata liste: */

brisi (lst); lst = NULL; break;

case 6: case 7: /* Citanje liste: */

printf ("Duzina? "); scanf ("%d", &n);

printf ("Elementi? "); brisi (lst);

switch (izbor) {

case 6: /* uz obrtanje redosleda brojeva: */

lst = citaj1 (n); break;

case 7: /* uz cuvanje redosleda brojeva: */

lst = citaj2 (n); break;

}

break;

case 8: /* Odredjivanje duzine liste: */

printf ("Duzina= %d\n", duz (lst)); break;

case 9: /* Ispisivanje liste: */

printf ("Lista= "); pisi (lst); putchar ('\n'); break;

case 0: /* Zavrsetak rada: */

kraj = 1; break;

default: /* Pogresan izbor: */

printf ("*** Neozvoljeni izbor! ***\a\n"); break;

}

}

}

/* Definicije funkcija za obradu lista (REKURZIVNO). */

int duz (Elem *lst) { /* Broj elemenata liste. */

return lst ? duz (lst->sled) + 1 : 0;

}

void pisi (Elem *lst) { /* Ispisivanje liste. */

if (lst) { printf ("%d ", lst->broj); pisi (lst->sled); }

}

Elem *na_pocetak (Elem *lst, int b) { /* Dodavanje na pocetak. */

Elem *novi = malloc (sizeof(Elem));

novi->broj = b; novi->sled = lst;

return novi;

17

}

Elem *na_kraj (Elem *lst, int b) { /* Dodavanje na kraj. */

if (!lst) {

lst = malloc (sizeof(Elem));

lst->broj = b; lst->sled = NULL;

} else

lst->sled = na_kraj (lst->sled, b);

return lst;

}

Elem *citaj1 (int n) { /* Citanje liste uz obrtanje redosleda. */

if (n == 0) return NULL;

else {

Elem *novi = malloc (sizeof(Elem));

novi->sled = citaj1 (n - 1); scanf ("%d", &novi->broj);

return novi;

}

}

Elem *citaj2 (int n) { /* Citanje liste uz cuvanje redosleda. */

if (n == 0) return NULL;

else {

Elem *novi = malloc (sizeof(Elem));

scanf ("%d", &novi->broj); novi->sled = citaj2 (n - 1);

return novi;

}

}

Elem *umetni (Elem *lst, int b) { /* Umetanje u uredjenu listu. */

if (!lst || lst->broj >= b) {

Elem *novi = malloc (sizeof(Elem));

novi->broj = b; novi->sled = lst;

return novi;

} else {

lst->sled = umetni (lst->sled, b);

return lst;

}

}

void brisi (Elem *lst) { /* Brisanje svih elemenata liste. */

if (lst) { brisi (lst->sled); free (lst); }

}

Elem *izostavi (Elem *lst, int b){ /* Izostavljanje svakog pojavljivanja. */

if (lst) {

if (lst->broj != b) {

lst->sled = izostavi (lst->sled, b);

} else {

Elem *stari = lst;

lst = izostavi (lst->sled, b);

free (stari);

}

}

return lst;

}

18

2.3.3. Zadaci

10. Napisati C program koji će vratiti koliko elemenata liste ima vrednost veću od elementa na početku liste.

#ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <stdlib.h> typedef struct elem { int broj; struct elem *sled; } Elem; int prebroj(Elem *lst) { int broj = 0; Elem *pom = lst; while (pom != NULL) { if (pom->broj > lst->broj) broj++; pom = pom->sled; } return broj; } Elem *citaj2(int n) { Elem *prvi = NULL, *posl = NULL; int i; for (i = 0; i < n;i++) { Elem *novi = (Elem*)malloc(sizeof(Elem)); scanf("%d", &novi->broj); novi->sled = NULL; if (!prvi) prvi = novi; else posl->sled = novi; posl = novi; } return prvi; } void main() { Elem *lst = NULL; int n, b; printf("\nBroj elemenata liste? "); scanf("%d", &n); lst = citaj2(n); b = prebroj(lst); printf("\nBroj elemenata liste koji su veci od prvoj je: %d\n", b); system("PAUSE"); }

19

11. Napisati C program koji vraća broj ponavljanja zadate vrednosti u listi.

#ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <stdlib.h> typedef struct elem { int broj; struct elem *sled; } Elem; Elem *citaj2(int n) { Elem *prvi = NULL, *posl = NULL; int i; for (i = 0; i < n;i++) { Elem *novi = (Elem*)malloc(sizeof(Elem)); scanf("%d", &novi->broj); novi->sled = NULL; if (!prvi) prvi = novi; else posl->sled = novi; posl = novi; } return prvi; } int brojPonavljanja(Elem *lst, int b) { Elem *pom = lst; int broj = 0; while (pom != NULL) { if (pom->broj == b) broj++; pom = pom->sled; } return broj; } void main() { Elem *lst = NULL; int n, b, br; printf("\nBroj elemenata liste? "); scanf("%d", &n); lst = citaj2(n); printf("\nTrazeni broj? "); scanf("%d", &br); b = brojPonavljanja(lst,br); printf("\nBroj elemenata liste koji su jednaki broju %d je: %d\n",br, b); system("PAUSE"); }

20

12. Napisati program koji u jednostruko povezanoj linearnoj listi pronalazi element koji se nalazi nakon elementa sa najmanjom vrednošću.

/* Program koji izbacuje element koji se nalazi iza elementa sa najmanjom vrednoscu. */ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <stdlib.h> typedef struct elem { int broj; struct elem *sled; } Elem; Elem *citaj2(int n) { Elem *prvi = NULL, *posl = NULL; int i; for (i = 0; i < n;i++) { Elem *novi = (Elem*)malloc(sizeof(Elem)); scanf("%d", &novi->broj); novi->sled = NULL; if (!prvi) prvi = novi; else posl->sled = novi; posl = novi; } return prvi; } void pisi(Elem *lst) { while (lst) { printf("%d ", lst->broj); lst = lst->sled; } } void izbaciNakonNajmin(Elem *lst) { Elem *minEl = lst, *pom = lst; while (pom) { if (pom->broj < minEl->broj) minEl = pom; pom = pom->sled; } if (minEl == NULL || minEl->sled == NULL) return; pom = minEl->sled; minEl->sled = minEl->sled->sled; free(pom); } void main() {

21

Elem *lst = NULL; int n; printf("\nBroj elemenata liste? "); scanf("%d", &n); lst = citaj2(n); printf("\nLista pre izbacivanja:\n"); pisi(lst); izbaciNakonNajmin(lst); printf("\nLista nakon izbacivanja:\n"); pisi(lst); system("PAUSE"); }

13. Napisati rekurzivnu funkciju koja će u jednostruko spregnutoj linearnoj listi prebrojati pozitivne elemente.

#pragma warning(disable: 4996) #include <stdio.h> #include <stdlib.h> typedef struct elem { int broj; struct elem *sled; } Elem; Elem *citaj2(int n) { Elem *prvi = NULL, *posl = NULL; Elem *novi; int i; for (i = 0; i < n; i++) { novi = (Elem*)malloc(sizeof(Elem)); scanf("%d", &novi->broj); novi->sled = NULL; if (!prvi) prvi = novi; else posl->sled = novi; posl = novi; } return prvi; } void pisi(Elem *lst) { while (lst) { printf("%d -> ", lst->broj); lst = lst->sled; } } int izbrojPozitivne(Elem *lst) { Elem *pom; pom = lst; if (pom == NULL) return 0; else { if (pom->broj > 0)

22

return 1 + izbrojPozitivne(pom->sled); else return izbrojPozitivne(pom->sled); } } void main() { Elem *lst = NULL; int n; printf("\nBroj elemenata liste? "); scanf("%d", &n); lst = citaj2(n); printf("\nLista pre izbacivanja elementa:\n"); pisi(lst); printf("\nBroj pozitivnih elemenata u listi je: %d\n", izbrojPozitivne(lst)); system("PAUSE");

}

14. Napisati funkciju koja će vratiti invertovano jednostruko spregnutu linearnu listu.

/* Napisati funkciju koja na osnovu zadate liste pravi drugu, sa elementima u obrnutom redosledu u odnosu na prvu. Lista se zadaje u datoteci na standardni nacin. */ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <stdlib.h> typedef struct elem { int broj; struct elem* sled; } Elem; Elem* ucitaj_listu_iz_datoteteke(FILE* ulaz) { int broj; Elem* prvi = NULL, * kraj, * tmp; fscanf(ulaz, "%d", &broj); prvi = (Elem*)malloc(sizeof(Elem)); prvi->broj = broj; prvi->sled = NULL; kraj = prvi; while (!feof(ulaz)) { fscanf(ulaz, "%d", &broj); tmp = (Elem*)malloc(sizeof(Elem)); tmp->broj = broj; tmp->sled = NULL; kraj->sled = tmp; kraj = tmp;

23

} return prvi; } void pisi(Elem* lst) { if (lst) { printf("%d ", lst->broj); pisi(lst->sled); } } /* RESENJE ZADATKA */ Elem* invertuj(Elem* lst) { Elem* lst2 = NULL; Elem* pom; while (lst != NULL) { pom = (Elem*)malloc(sizeof(Elem)); pom->broj = lst->broj; pom->sled = lst2; lst2 = pom; lst = lst->sled; } return lst2; } int main() { FILE* ulaz; Elem* lst = NULL, *lst2; ulaz = fopen("zad15.txt", "r"); lst = ucitaj_listu_iz_datoteteke(ulaz); fclose(ulaz); printf("\nUcitana lista:\n"); pisi(lst); printf("\n"); lst2 = invertuj(lst); printf("\nInvertovana lista:\n"); pisi(lst2); printf("\n"); return 0; }

15. Metoda koja koja pravi indentičnu kopiju date JSListe

/* Napisati metodu koja pravi istovetnu kopiju date liste. Lista je zadata standardno u datoteci. */ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h>

24

#include <stdlib.h> typedef struct elem { int broj; struct elem* sled; } Elem; Elem* ucitaj_listu_iz_datoteteke(FILE* ulaz) { int broj; Elem* prvi = NULL, * kraj, * tmp; fscanf(ulaz, "%d", &broj); prvi = (Elem*)malloc(sizeof(Elem)); prvi->broj = broj; prvi->sled = NULL; kraj = prvi; while (!feof(ulaz)) { fscanf(ulaz, "%d", &broj); tmp = (Elem*)malloc(sizeof(Elem)); tmp->broj = broj; tmp->sled = NULL; kraj->sled = tmp; kraj = tmp; } return prvi; } void pisi(Elem* lst) { if (lst) { printf("%d ", lst->broj); pisi(lst->sled); } } /* RESENJE ZADATKA */ Elem* kopirajListu(Elem* lst) { Elem* lst2 = NULL; Elem *newEl, *pom; while (lst != NULL) { newEl = (Elem*)malloc(sizeof(Elem)); newEl->broj = lst->broj; newEl->sled = NULL; if (lst2 == NULL) lst2 = newEl; else { pom = lst2; while (pom->sled != NULL) pom = pom->sled; pom->sled = newEl; } lst = lst->sled; } return lst2; } int main() { FILE* ulaz;

25

Elem* lst = NULL, * lst2; ulaz = fopen("zad16.txt", "r"); lst = ucitaj_listu_iz_datoteteke(ulaz); fclose(ulaz); printf("\nUcitana lista:\n"); pisi(lst); printf("\n"); lst2 = kopirajListu(lst); printf("\nKopirana lista:\n"); pisi(lst2); printf("\n"); return 0; }

16. Metoda koja od JSListe pravi niz

public int[] transform(JSLista l) { int br = 0;

CvorJSListe pomocni = l.prvi;

while (pomocni != null) { br++;

pomocni = pomocni.sledeci;

}

pomocni = l.prvi;

int[] stack = new int[br];

for (int i = 0; i < stack.length; i++) { stack[i] = pomocni.podatak;

pomocni = pomocni.sledeci;

}

return stack;

}

17. Napisati program koji od date dve jednostruko spregnute liste pravi trecu koja predstavlja uniju elemenata date dve liste.

#ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <stdlib.h> typedef struct elem { int broj; struct elem *sled; } Elem;

26

Elem *citaj2(int n) { Elem *prvi = NULL, *posl = NULL; int i; for (i = 0; i < n;i++) { Elem *novi = (Elem*)malloc(sizeof(Elem)); scanf("%d", &novi->broj); novi->sled = NULL; if (!prvi) prvi = novi; else posl->sled = novi; posl = novi; } return prvi; } void pisi(Elem *lst) { while (lst) { printf("%d ", lst->broj); lst = lst->sled; } } int postoji(Elem *lst, int b) { while (lst) { if (lst->broj == b) return 1; lst = lst->sled; } return 0; } Elem* unija(Elem *lst1, Elem *lst2) { Elem *novaLista = NULL, *pom; if (lst1 == NULL && lst2 == NULL) return NULL; while (lst1 != NULL) { if (!postoji(novaLista, lst1->broj)) { pom = (Elem*)malloc(sizeof(Elem)); pom->broj = lst1->broj; pom->sled = novaLista; novaLista = pom; } lst1 = lst1->sled; } while (lst2 != NULL) { if (!postoji(novaLista, lst2->broj)) { pom = (Elem*)malloc(sizeof(Elem)); pom->broj = lst2->broj; pom->sled = novaLista; novaLista = pom; } lst2 = lst2->sled; }

27

return novaLista; } void main() { Elem *lst1 = NULL, *lst2 = NULL, *lst3 = NULL; int n1, n2; printf("\nBroj elemenata prve liste? "); scanf("%d", &n1); lst1 = citaj2(n1); printf("\nBroj elemenata druge liste? "); scanf("%d", &n2); lst2 = citaj2(n2); lst3 = unija(lst1, lst2); printf("\nPrva lista:\n"); pisi(lst1); printf("\nDruga lista:\n"); pisi(lst2); printf("\nUnija:\n"); pisi(lst3); system("PAUSE");

}

2.3.4. KRUŽNO SPREGNUTA LINEARNA LISTA

Kod ovog tipa liste pokazivač poslednjeg elementa nema NULL vrednost već ukazuje na prvi

element liste. Prednosti ovog tipa liste su:

- Svakom elementu se može pristupiti polazeći od bilo kog elementa liste.

- Jednostavnije pronalaženje elemenata liste.

- Efikasnije operacije dodavanja i brisanja elemenata iz liste.

Kod ovog tipa liste neophodno je znati prvi ili poslednji element. To možemo rešiti na dva

načina:

1) Pokazivač na glavu liste

2) Označavanje početnog elementa liste. Npr. ostaviti prazno polje za podatak, ili u njega

upisati specijalni simbol... itd.

28

2.3.5. DVOSTRUKO POVEZANA LINEARNA LISTA

Kod ovog tipa listi, svaki element ima dva pokazivača koji ukazuju na prethodni i naredni

element (levi i desni ukazivač).

Levi ukazivač krajnjeg levog i desni pokazivač krajnjeg desnog elementa su NULL.

18. NCP koji iz neprazne datoteke broj.txt ucitava paran broj celih brojeva x[1], x[2],...,x[n], gde n nije unapred poznato. Ispisati poruku na standardni izlaz da li vazi da x[1]==x[n], x[2]==x[n-1],..., x[k]=x[k+1], k=1..n/2

#include <stdio.h>

#include <stdlib.h>

typedef struct listacv dvlista;

29

/*dvostruko povezana linearna lista*/

struct listacv{

int broj; /*informaciono polje je clan niza x*/

dvlista *sledeci, *prethodni;

};

dvlista *ubaci(dvlista *kraj, int broj){

dvlista *novi; /*novi cvor za umetanje u listu*/

novi=(dvlista *)malloc(sizeof(dvlista)); /*alokacija memorije za novi

cvor liste*/

novi->broj=broj; /*inicijalizacija polja broj u cvoru novi */

novi->sledeci=NULL;

novi->prethodni=kraj; /*novi element se dodaje na kraj postojece liste*/

kraj->sledeci=novi; /* do tada poslednji cvor kraj je ispred clana

novi*/

kraj=novi; /*poslednji dodat element je novi kraj liste*/

return kraj;

}

main()

{

dvlista *prvi, *kraj; /*prvi i poslednji cvor dvostruke liste*/

int n; /*broj ucitanih celih brojeva*/

int broj; /*tekuci broj sa ulaza*/

int jednak; /*indikator jednakosti dva clana - cuva vrednost 0 ili 1 */

FILE *ulaz;

int i; /*brojacka promenljiva*/

/*otvaranje datoteke za citanje i upis brojeva iz datoteke u

dvostruku povezanu listu */

ulaz=fopen("broj.txt", "r");

fscanf(ulaz, "%d", &broj);

/*kreiranje prvog cvora liste za prvi ucitan broj */

prvi=(dvlista*)malloc(sizeof(dvlista));

prvi->broj=broj;

prvi->sledeci=NULL;

prvi->prethodni=NULL;

kraj=prvi; n=1; //za sada ucita je samo jedan clan

/*formiranje liste od brojeva koji se ucitavaju sve do kraja datoteke -

feof(ulaz) */

while (!feof(ulaz))

{ fscanf(ulaz, "%d", &broj);

kraj=ubaci (kraj, broj);

n++;

}

fclose(ulaz);

/*testiranje jednakih parova uz pomeranje tekuceg prvog clana i tekuceg

poslednjeg

clana ka sredini liste*/

jednak=1;

for(i=1; i<=n/2 && jednak; i++)

{ jednak=jednak&&(prvi->broj==kraj->broj);

prvi=prvi->sledeci; /*pomeranje ka sredini liste */

30

kraj=kraj->prethodni; /*pomeranje ka sredini liste */

}

printf("Za unete broje jednakost ");

if(!jednak) printf("ne ");

printf("vazi\n");

return 0;

}

2.3.6. PRIMENE SPREGNUTIH LINEARNIH LISTI

Prikaz polinoma

Pretpostavimo da imamo polinom od tri promenljive: 3X2 + 4XY + Y2 + XYZ

Svaki element liste koji prikazuje jedan član polinoma dat je na sledećoj slici:

Spregnuta lista za prikaz ovog polinoma izgledala bi:

Odgovarajuća struktura elementa bi bila:

typedef struct elem element;

struct elem{

int expx;

int expy;

int expz;

int koef;

element *naredni;

}

19. NCP-e za sabiranje, oduzimanje i množenje polinoma jedne promenljive. /* NCP za sabiranje, oduzimanje i mnozenje polinoma jedne promenljive. */ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <stdlib.h> #include <math.h> struct pnode { double coef; long power; struct pnode *link; }; typedef struct pnode poly; #define COEF(a) ((a)->coef) #define POWER(a) ((a)->power) #define LINK(a) ((a)->link) poly *addElem(poly *p, double coef, long pow) { poly *c, *newel, *pret = NULL;

31

c = p; while (c && POWER(c) > pow) { pret = c; c = LINK(c); } if (!c || POWER(c) < pow) { newel = (poly*)malloc(sizeof(poly)); COEF(newel) = coef; POWER(newel) = pow; LINK(newel) = c; if (!pret) p = newel; else LINK(pret) = newel; return p; } if (POWER(c) == pow) { COEF(c) += coef; return p; } return p; } poly *create() { int pow; double c; poly *p = NULL; printf("\nUnesite polinom pocev od najveceg stepena:\n"); while (1) { printf("\nKoeficijent: "); scanf("%lf", &c); printf("\nStepen: "); scanf("%d", &pow); if (c == 0) break; p = addElem(p, c, pow); if (pow == 0) break; } return p; } void display(poly *p) { poly *cur = p; if (cur) { printf(" %c %.2f * X^%d", COEF(cur) < 0 ? '-' : ' ', fabs(COEF(cur)), POWER(cur)); cur = LINK(cur); } while(cur != NULL){ printf(" %c %.2f * X^%d", COEF(cur) < 0 ? '-' : '+', fabs(COEF(cur)), POWER(cur)); cur = LINK(cur); } } void brisi(poly *p) { poly *stari; while (p) { stari = p; p = LINK(p);

32

free(stari); } } poly *polyadd(poly *p1, poly* p2) { poly *p, *res = NULL; p = p1; while (p) { res = addElem(res, COEF(p), POWER(p)); p = LINK(p); } p = p2; while (p) { res = addElem(res, COEF(p), POWER(p)); p = LINK(p); } return res; } poly *polysub(poly *p1, poly* p2) { poly *p, *res = NULL; p = p1; while (p) { res = addElem(res, COEF(p), POWER(p)); p = LINK(p); } p = p2; while (p) { res = addElem(res, -COEF(p), POWER(p)); p = LINK(p); } return res; } poly *polymul(poly *p1, poly *p2) { poly *pom1, *pom2, *res = NULL; pom1 = p1; while (pom1) { pom2 = p2; while (pom2) { res = addElem(res, COEF(pom1)*COEF(pom2), POWER(pom1) + POWER(pom2)); pom2 = LINK(pom2); } pom1 = LINK(pom1); } return res; } poly *polydif(poly* p1) { poly *pom1, *res = NULL; pom1 = p1; while (pom1) { if (POWER(pom1) != 0) res = addElem(res, COEF(pom1)*POWER(pom1), POWER(pom1) - 1); pom1 = LINK(pom1); } return res; } void main() { int ch; poly *poly1, *poly2, *poly3; poly1 = poly2 = poly3 = NULL;

33

printf("\nUnesite prvi polinom:\n"); poly1 = create(); display(poly1); printf("\nUnesite drugi polinom:\n"); poly2 = create(); display(poly2); while (1) { printf("\n************************\n" "\nOdaberi operaciju:\n" "1.Sabiranje\n" "2.Oduzimanje\n" "3.Mnozenje\n" "4.Izvod\n" "5.Izlaz\n"); scanf("%d", &ch); switch (ch) { case 1: poly3 = polyadd(poly1, poly2); display(poly3); break; case 2: poly3 = polysub(poly1, poly2); display(poly3); break; case 3: poly3 = polymul(poly1, poly2); display(poly3); break; case 4: poly3 = polydif(poly1); display(poly3); break; default: exit(1); } } brisi(poly1); brisi(poly2); brisi(poly3); system("PAUSE"); }

20. Napisati C program koji prebacuje čvor sa najvećom vrednošću na početak dvostruko spregnute linearne liste. Elementi liste se nalaze u datoteci broj.txt.

#ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <stdlib.h> typedef struct listacv DSLlista; struct listacv { int broj; DSLlista *sledeci; DSLlista *prethodni; };

34

DSLlista *ubaci(DSLlista *kraj, int broj) { DSLlista *novi; novi = (DSLlista *)malloc(sizeof(DSLlista)); novi->broj = broj; novi->sledeci = NULL; novi->prethodni = kraj; kraj->sledeci = novi; kraj = novi; return kraj; } void print(DSLlista *pocetak) { DSLlista *pom = pocetak; while (pom) { printf("%d -> ", pom->broj); pom = pom->sledeci; } printf("NULL\n"); } //Funkcija vraca pokazivac na prvi element DSLliste DSLlista *prebaciMaxElement(DSLlista *kraj) { DSLlista *pom = kraj, *max = kraj; while (pom->prethodni != NULL) { pom = pom->prethodni; if (pom->broj > max->broj) { max = pom; } } if (max->prethodni == NULL) return max; max->prethodni->sledeci = max->sledeci; if (max->sledeci != NULL) max->sledeci->prethodni = max->prethodni; max->prethodni = NULL; max->sledeci = pom; pom->prethodni = max; return max; } void main() { DSLlista *prvi, *kraj; int n, broj; FILE *ulaz; ulaz = fopen("broj.txt", "r"); fscanf(ulaz, "%d", &broj); prvi = (DSLlista*)malloc(sizeof(DSLlista)); prvi->broj = broj; prvi->sledeci = NULL; prvi->prethodni = NULL; kraj = prvi; n = 0; while (!feof(ulaz)) { fscanf(ulaz, "%d", &broj); kraj = ubaci(kraj, broj); n++; }

35

fclose(ulaz); print(prvi); prvi = prebaciMaxElement(kraj); print(prvi); system("PAUSE"); }

21. Dat je pokazivač p na neki čvor dvostruko spregnute linearne liste. Napisati metodu koja prikazuje onu polovinu liste koja ima veći zbir, u odnosu na element na koga ukazuje p.

#ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <stdlib.h> typedef struct listacv DSLlista; struct listacv { int broj; DSLlista *sledeci; DSLlista *prethodni; }; DSLlista *ubaci(DSLlista *kraj, int broj) { DSLlista *novi; novi = (DSLlista *)malloc(sizeof(DSLlista)); novi->broj = broj; novi->sledeci = NULL; novi->prethodni = kraj; kraj->sledeci = novi; kraj = novi; return kraj; } int veciZbir(DSLlista *p) { DSLlista *pom; int sumaLevo, sumaDesno; sumaLevo = 0; sumaDesno = 0; pom = p->prethodni; while (pom != NULL) { sumaLevo += pom->broj; pom = pom->prethodni; } pom = p->sledeci; while (pom != NULL) { sumaDesno += pom->broj; pom = pom->sledeci; } if (sumaLevo > sumaDesno) return 1; else if (sumaLevo < sumaDesno) return -1; else return 0;

36

} void prikaziPodlistu(DSLlista *p, int veci) { DSLlista *pom=NULL; if (veci == 0) { printf("Zbir elemenata u listama s leva i s desna je jednak!"); return; } if (veci == 1) pom = p->prethodni; else if(veci == -1) pom = p->sledeci; while (pom != NULL) { printf("%d -> ", pom->broj); if (veci == 1) pom = pom->prethodni; else if (veci == -1) pom = pom->sledeci; } printf("NULL\n"); } void print(DSLlista *pocetak) { DSLlista *pom = pocetak; while (pom) { printf("%d -> ", pom->broj); pom = pom->sledeci; } printf("NULL\n"); } void main() { DSLlista *prvi, *kraj, *sredina=NULL; int n, broj; FILE *ulaz; ulaz = fopen("broj.txt", "r"); fscanf(ulaz, "%d", &broj); prvi = (DSLlista*)malloc(sizeof(DSLlista)); prvi->broj = broj; prvi->sledeci = NULL; prvi->prethodni = NULL; kraj = prvi; n = 0; while (!feof(ulaz)) { fscanf(ulaz, "%d", &broj); kraj = ubaci(kraj, broj); if (n == 4) sredina = kraj; n++; } fclose(ulaz); print(prvi); printf("\nVrednost u sredini je: %d\n", sredina->broj); prikaziPodlistu(sredina,veciZbir(sredina)); system("PAUSE");

37

}

22. Dat je pokazivač na prvi element dvostruko spregnute liste čiji elementi predstavljaju elemente reči (slovo ili prazno mesto). Napisati metodu koja proverava da li je data rečenica (reč) palindrom. Palindrom je reč (rečenica) koja se čita isto s obe strane (i sleva na desno i s desna na levo).

#pragma warning(disable: 4996) #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct listacv dvlista; struct listacv { char ch; dvlista *prethodni; dvlista *sledeci; }; dvlista *ubaci(dvlista *kraj, char c) { dvlista *novi; novi = (dvlista *)malloc(sizeof(dvlista)); novi->ch = c; novi->sledeci = NULL; novi->prethodni = kraj; kraj->sledeci = novi; kraj = novi; return kraj; } int daLiJePalindromDS(dvlista *poc, dvlista *kraj) { dvlista *pom1 = poc, *pom2 = kraj; if (poc == NULL) return 1; //ignorisemo razmake na pocetku i kraju while (pom1 != NULL && pom1->ch == ' ') pom1 = pom1->sledeci; while (pom2 != NULL && pom2->ch == ' ') pom2 = pom2->prethodni; while (pom2 != NULL || pom1 != NULL) { if (pom1->ch == ' ') pom1 = pom1->sledeci; if (pom2->ch == ' ') pom2 = pom2->prethodni; if (pom1->ch == pom2->ch) { pom1 = pom1->sledeci; pom2 = pom2->prethodni; } else { return 0; } } return 1; } void main() { char recenica[] = "ANA VOLI MILOVANABBB"; dvlista *pocrec = NULL, *krajrec = NULL, *novi, *pom; int i, pal; novi = (dvlista *)malloc(sizeof(dvlista));

38

novi->ch = recenica[0]; novi->prethodni = NULL; novi->sledeci = NULL; pocrec = novi; krajrec = novi; for (i = 1; i < (int)strlen(recenica); i++) krajrec = ubaci(krajrec, recenica[i]); pom = pocrec; while (pom) { printf("%c -> ", pom->ch); pom = pom->sledeci; } pal = daLiJePalindromDS(pocrec, krajrec); if (pal) printf("\nRECENICA JE PALINDROM\n"); else printf("\nRECENICA NIJE PALINDROM\n"); system("PAUSE"); }

23. Data je dvostruko spregnuta (DS) lista čiji su elementi čvorova pokazivači na početak jednostruko spregnute (JS) liste. Napisati klasu koja opisuje čvor ovakve DS liste, a zatim napisati algoritam za ubacivanje novog elementa u ovako definisanu strukturu, koji funkcioniše po sledećem principu: kreće se od početka DS liste. Ako je element koji se ubacuje manji od prvog elementa JS liste trenutnog čvora DS liste, onda se taj element ubacuje na kraj te JS liste. U suprotnom, prelazi se na sledeći čvor DS liste i algoritam se ponavlja. Ako se stigne do kraja DS liste, onda se kreira novi čvor i u njegovu JS listu se ubacuje novi element. Početna metoda prihvata pokazivač na početak DS liste i ceo broj koji se ubacuje.

public class CvorListe { private CvorListe sledeci; private CvorListe prethodni; private CvorJSListe prvi;

public void ubaci(CvorListe p, int pod) {

if (p == null) {

p = new CvorListe();

p.prvi = new CvorJSListe(pod, null); } else {

while (p.sledeci != null && p.prvi.podatak <= pod) {

p = p.sledeci;

}

if (p.prvi.podatak <= pod) { p.sledeci =

new CvorListe(); p.sledeci.prethodni

= p;

p.sledeci.prvi = new CvorJSListe(pod, null);

39

} else {

CvorJSListe pom = prvi;

while (pom.sledeci != null) {

pom = pom.sledeci;

}

pom.sledeci = new CvorJSListe(pod, null);

}

}

}

}

2.3.7. Dat je pokazivac na neki element DS liste celih brojeva. Napisati funkciju koja ce izbaciti iz liste onaj element koji sadrži najmanju vrednost u listi.

public int izbaciMin(CvorDSListe cvor) { if (cvor == null)

return Integer.MAX_VALUE;

while (cvor.prethodni != null) { cvor = cvor.prethodni;

}

CvorDSListe min = minElement(cvor);

if (min.prethodni != null) min.prethodni.sledeci =

min.sledeci;

if (min.sledeci != null)

min.sledeci.prethodni = min.prethodni;

return min.podatak;

}

2.3.8. Dat je pokazivač na prvi element DS liste. Napisati metodu koja vraća

pokazivač na element sa najmanjom vrednošću u listi.

private CvorDSListe minElement(CvorDSListe cvor) { CvorDSListe

min = cvor;

while (cvor != null) {

if (cvor.podatak < min.podatak)

min = cvor;

cvor = cvor.sledeci;

}

return min;

}

2.3.9. Dat je pokazivac na pocetni cvor DS liste sortirane u rastucem redosledu koja sadrži pozitivne cele brojeve. Napisati funkciju koja ce

40

izmedju svih onih elementa liste koji se po vrednosti razlikuju za više od 1 ubaciti u datu listu nove elemente tako da lista posle poziva operacije ima u

sebi sukcesivne cele brojeve. Na primer ako lista sadrži 3, 5, 8 nakon poziva ove opercije sadržace 3, 4, 5, 6, 7, 8

public void popuni(CvorDSListe prvi) { if (prvi == null || prvi.sledeci == null)

return;

if ((prvi.sledeci.podatak - prvi.podatak) > 1) {

CvorDSListe novi = new CvorDSListe(prvi.podatak + 1, prvi, prvi.sledeci);

prvi.sledeci.prethodni = novi;

prvi.sledeci = novi;

}

popuni(prvi.sledeci);

}

2.3.10. Dat je pokazivac na neki cvor DS liste celih brojeva koja sigurno sadrži

najmanje 4 elementa. Napisati funkciju ubaciNti(int A, int N) koja ce ubaciti novi element sa sadržajem A i to tako da on bude na N-toj poziciji

od pocetka.

public void ubaciN(CvorDSListe cvor, int a, int n) { if (cvor == null)

return;

while (cvor.prethodni != null) cvor

= cvor.prethodni;

CvorDSListe pom = new CvorDSListe(a, null, null); if (n < 0) {

return;

}

if (n == 0) { pom.prethodni

= null; pom.sledeci =

cvor;

if (pom.sledeci != null) pom.sledeci.prethodni

= pom;

}

int i = 0; while (i++ < n - 1) {

if (cvor.sledeci == null) return;

cvor = cvor.sledeci;

}

pom.prethodni = cvor;

pom.sledeci = cvor.sledeci; if (cvor.sledeci != null)

cvor.sledeci.prethodni = pom; cvor.sledeci =

pom;

41

}

2.3.11. Metoda koja invertuje dvostruko-spregnutu listu.

public CvorDSListe invertujLIstu(CvorDSListe cvor) {

CvorDSListe c = null;

while (cvor != null) {

c = new CvorDSListe(cvor.podatak, null, c);

if (c.sledeci != null)

c.sledeci.prethodni = c;

cvor = cvor.sledeci;

}

return c; }

2.3.12. Metoda koja vraća DSListu koja predstavlja razliku (skupova) dve DSListe

public CvorDSListe razlika(CvorDSListe c1, CvorDSListe c2) {

if (c1 == null || c2 == null) return c1;

CvorDSListe novi = null; CvorDSListe pom = c1; while (pom != null) {

if (!pronadji(pom.podatak, c2)) {

novi = new CvorDSListe(pom.podatak, null, novi);

if (novi.sledeci != null) { novi.sledeci.prethodni =

novi;

}

}

pom = pom.sledeci;

}

return novi;

}

2.3.13. Metoda koja poslednji element DSListe prebacuje na početak te DSListe.

Dati su pokazivačni na prvi i poslednji element

public void prebaciPoslednjiNaPocetak() { if (prvi == null) {

return;

}

if (prvi.sledeci == null) {

return;

}

CvorDSListe pom = poslednji;

42

poslednji = pom.prethodni; pom.sledeci =

prvi; prvi.prethodni = pom;

pom.prethodni.sledeci = null;

pom.prethodni = null;

prvi = pom;

}

2.3.14. Metoda koja računa zbir elemenata koji se ponavljaju. Primer: Ulaz 5 7 2 2

5 2. Rezultat je 2+5=7

public int ZbirDuplih(CvorDSListe p1) { if (p1 == null)

return 0; CvorDSListe tek = p1;

CvorDSListe tek1 = p1.sledeci;

CvorDSListe tek2 = tek.prethodni; int ima

= 0;

int suma = 0;

for (; tek != null; tek = tek.sledeci) {

for (; tek2 != null; tek2 = tek2.prethodni) {

if (tek.podatak == tek2.podatak) {

ima = 1;

break;

}

}

tek2 = tek;

if (ima == 0) {

for (; tek1.sledeci != null; tek1 = tek1.sledeci) {

if (tek.podatak == tek1.podatak) {

suma = suma + tek.podatak;

break;

}

}

}

if (tek1.sledeci != null) {

tek1 = tek1.sledeci;

}

}

return suma; }

2.3.15. Metoda koja vraća pokazivač na prvi element ciklične DSListe sortirane u rastućem redosledu. Dat je pokazivač na neki element u listi.

43

public CvorDSListe vratiPokNaPrviEl(CvorDSListe p) {

if (p == null || (p.sledeci == p && p.prethodni == p))

return p; CvorDSListe pom = p;

while (pom.sledeci.podatak > pom.podatak

&& pom.prethodni.podatak < pom.podatak)

pom = pom.sledeci;

return pom.sledeci;

}

44

RETKO POSEDNUTE MATRICE

Realizuje se preko spregnutih listi i ima za cilj smanjenje broja izračunavanja, kao i smanjenje

potrebne memorije za čuvanje sturkture.

Jedna moguća reprezentacija dvodimenzionalne retko posednute matrice bi bila preko jedne

spregnute liste za svaki red i svaku kolonu matrice. Svaka od ovih listi ima zaglavlje, tj. glavu. Svakom

nenultom mestu u matrici odgovara jedan čvor oblika:

struct cvor {

int red;

int kolona;

int vrednost;

struct cvor *nar_u_kol;

struct cvor *nar_u_vrsti;

}

/* Implementacija dinamickih kvadratnih matrica preko pokazivaca na niz pokazivaca na

kolone */

#include <stdio.h>

#include <stdlib.h>

/* Tip elemenata matrice ce biti float */

typedef float tip;

/* Funkcija alocira matricu dimenzije n*n */

tip** allocate(int n){

tip **m;

int i;

/* Alocira se niz pokazivaca na kolone */

m=(tip**)malloc(n*sizeof(tip*));

for (i=0; i<n; i++)

/* Alociraju se nizovi brojeva koji predstavljaju kolone */

45

m[i]=(tip*)malloc(n*sizeof(tip));

return m;

}

/* Funkcija vrsi dealociranje date matrice dimenzije n */

void deallocate(tip** m, int n){

int i;

/* Uklanjaju se kolone */

for (i=0; i<n; i++)

free(m[i]);

/* Uklanja se niz pokazivaca na kolone */

free(m);

}

/* Funkcija ispisuje datu matricu dimenzije n */

void IspisiMatricu(tip** matrica, int n){

int i,j;

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

for(j=0; j<n; j++)

printf("%f\t",matrica[i][j]);

printf("\n");

}

}

/* Funkcija ucitava datu matricu i njenu dimenziju */

void UnesiMatricu(tip*** matrica,int *pn){

int i,j;

printf("Unesite dimenziju kvadratne matrice :");

scanf("%d",pn);

*matrica=allocate(*pn);

for (i=0; i<*pn; i++)

for(j=0; j<*pn; j++){

printf("A[%d][%d]=",i+1,j+1);

scanf("%f",&((*matrica)[i][j]));

}

}

/* Rekurzivna funkcija koja vrsi Laplasov razvoj - neefikasna, ali dobra vezba */

tip determinanta(tip** matrica, int n){

int i;

tip** podmatrica;

tip det=0;

int vrsta, kolona, znak;

/* Izlaz iz rekurzije je matrica 1x1 */

if (n==1)

return matrica[0][0];

/* Podmatrica ce da sadrzi minore polazne matrice */

podmatrica=allocate(n-1);

znak=1;

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

int vrsta,kolona;

for (kolona=0; kolona<i; kolona++)

for(vrsta=1; vrsta<n; vrsta++)

podmatrica[vrsta-1][kolona]=matrica[vrsta][kolona];

for (kolona=i+1; kolona<n; kolona++)

for(vrsta=1; vrsta<n; vrsta++)

podmatrica[vrsta-1][kolona-1]=matrica[vrsta][kolona];

det+=znak*matrica[0][i]*determinanta(podmatrica,n-1);

znak*=-1;

}

deallocate(podmatrica,n-1);

return det;

}

main(){

46

/* Matrica i njena dimenizija */

tip **matrica;

int n;

/* Unosi se matrica */

UnesiMatricu(&matrica,&n);

/* Ispisuje se determinanta */

printf("Determinanta je : %f\n",determinanta(matrica,n));

return 0;

}

2.4. BSTEK

2.4.1. 6BDEFINICIJE I STUKTURA

Stek (stack) ili magacin je jedna od najznačajnijih linearnih i dinamičkih struktura podataka. Stavljanje

(upis) ili brisanje (čitanje) elemenata se vrši samo na jednom kraju. Operacija stavljanja se naziva PUSH, a

brisanja POP. Stek se prazni i puni po tzv. LIFO strategiji.

#define MAXSTACKLEN 100 /* maksimalna dubina steka */

int stack[MAXSTACKLEN];/*vrednost steka */

int stackPointer;/*naredna slobodna pozicija na steku */

/* push: gurni na stek */

void push( int arg ){

if( stackPointer < MAXSTACKLEN )

stack[stackPointer++] = arg;

else

printf("greska: stek je pun, nemoguce je smestiti %g\n", arg);

}

/* pop: skini sa steka*/

int pop( void ){

if( stackPointer > 0 )

return stack[--stackPointer];

else{

printf("greska: stek je prazan\n" );

return 0;

}

}

int top(void){

if(stackPointer > 0)

return stack[stackPointer-1];

else{

printf("greska: stek je prazan\n");

return 0;

}

}

10BOpis zadatka:

Za aritmetičke izraze kao što su T2+3T, T2+3*4T, T(2+3)*4T, itd. sa kojima se tipično susrećemo kako u

matematici tako i u programiranju, kažemo da su zapisani u Tinfiksnoj formiT, s obzirom da se operator

nalazi TizmeđuT operanada. Najveća mana ovakvog zapisa leži u činjenici da je neophodno uspostaviti

47

konvencije o TprioritetuT pojedinih operatora (npr. konvenciju da množenje ima veći prioritet u odnosu na

sabiranje), i eventualno koristiti zagrade za promenu prioriteta.

U računarstvu je od velikog značaja jedan posve drugačiji zapis aritmetičkih izraza, poznat pod

imenom TpostfiksnaT ili Tobrnuta poljska notacijaT, u kojem se operatori navode Tnakon T operanada (naziv

obrnuta poljska notacija dat je u čast poljskog matematičara Jana Lukasiewitza, koji je predložio upravo

obrnuti princip - da se operatori zapisuju TispredT operanada). Na primer, prethodna tri izraza zapisana u

infiksnoj notaciji, u obrnutoj poljskoj notaciji zapisuju se ovako:

2 3 +

2 3 4 * + 4 * 3 + 2

2 3 + 4 * (2 + 3) * 4

Izračunavanje izraza u obrnutoj poljskoj notaciji izvodi se tako da se svaki operand na koji se

naiđe dodaje na kraj liste (koja je na početku prazna), dok se prilikom nailaska na binarni operator

poslednja dva elementa iz liste brišu i zamenjuju rezultatom operacije (sličan princip može se

primeniti i na n-arne operatore). Broj koji na kraju izračunavanja ostane u listi predstavlja rezultat

izračunavanja (taj broj mora biti jedinstven ako je izraz bio ispravan). Uzmimo na primer, sledeći

izraz zapisan u obrnutoj poljskoj notaciji:

3 5 6 4 - * 6 + * 2 /

Izračunavanje ovog izraza teklo bi ovako:

3

3 5

3 5 6

3 5 6 4

3 5 2 T6 i 4 su zamijenjeni sa 6-4=2

3 10 T5 i 2 su zamijenjeni sa 5*2=10

3 10 6

3 16 T10 i 6 su zamijenjeni sa 10+6=16

48 T3 i 16 su zamijenjeni sa 3*16=48

48 2

24 T48 i 2 su zamijenjeni sa 48/2=24

Dakle, rezultat izračunavanja je 24. Jedna od glavnih prednosti obrnute poljske notacije leži u činjenici

da nije potrebno voditi računa o prioritetu operacija, kao i da je Tsvaki T izraz moguće zapisati Tbez upotrebe

zagradaT. Na primer, izraz koji bi se u infiksnom obliku zapisao kao

((3 + 5) / (7 - 2) + 4 / (8 - 5)) / (3 + 5 * (4 / (7 - 2)))

u obrnutoj poljskoj notaciji zapisuje se kao

3 5 + 7 2 - / 4 8 5 - / + 3 5 4 7 2 - / * + /

Stoga, kompajleri većine programskih jezika interno prevode sve aritmetičke izraze u obrnutu poljsku

notaciju pre bilo kakve dalje obrade. Vaš zadatak je da napravite program koji aritmetičke izraze u

infiksnom obliku koji se sastoje od jednoslovnih promjenljivih (a-z), četiri računske operacije ( T+T, T-T, T*T i T/T), i

eventualno zagrada, pretvori u obrnutu poljsku notaciju.

48

11BUlazna datoteka:

U prvom i jedinom redu ulazne tekstualne datoteke TRPN.INT nalazi se niz znakova koji predstavlja

aritmetički izraz u infiksnoj notaciji koji treba pretvoriti u postfiksnu odnosno obrnutu poljsku notaciju.

Ispravan aritmetički izraz sme sadržavati samo jednoslovne promenljive pisane malim slovima (od "a" do

"z"), binarne operatore " T+T", "T-T", "T*T" i "T/T" kao i male zagrade "(" i ")" za promenu prioriteta operacija.

Ispravan aritmetički izraz neće sadržati nikakve razmake.

12BIzlazna datoteka:

Ukoliko ulazna datoteka sadrži ispravan aritmetički izraz u infiksnoj notaciji, tada u prvi i jedini red

izlazne tekstualne datoteke TRPN.OUT T treba upisati niz znakova koji predstavlja aritmetički izraz u obrnutoj

poljskoj notaciji. Ovaj izraz sastoji se od niza znakova koji smeju sadržavati samo mala slova (od "a" do

"z") i binarne operatore "T+T", "T- T", "T*T" i "T/T". Nikakvi razmaci u izrazu nisu dozvoljeni. Ukoliko ulazna datoteka

ne sadrži ispravan aritmetički izraz, u izlaznu datoteku treba samo ispisati tekst.

NEISPRAVAN IZRAZ

13BPrimeri:

TRPN.IN

x+y+z

TRPN.OUT

xy+z+

TRPN.IN

x+(y+z)

TRPN.OUT

xyz++

TRPN.IN

x+y*z

TRPN.OUT

xyz*+

TRPN.IN

(x+y)*z

TRPN.OUT

xy+z*

TRPN.IN

x*(y+z

TRPN.OUT

NEISPRAVAN IZRAZ

T

RPN.IN

a+b/c+d

TRPN.OUT

abc/+d+

TRPN.IN

(a+b)/(c+d)

TRPN.OUT

ab+cd+/

TRPN.IN

x*(y+z*(a+b*(c-d))/e)

TRPN.OUT

xyzabcd-*+*e/+*

/******************************************

* IZRACUNAVANJE IZRAZA KOJI JE ZADAT U *

* INVERZNOJ POLJSKOJ NOTACIJI *

*******************************************/

int computePolish(char izraz[]){

char symbol;

int sum,n,i;

n=(int)strlen(izraz);

i=0;

while (i<n){

symbol=izraz[i];

if((symbol - '0')>=0 && (symbol - '0')<=9){

push(symbol-'0');

}

if ( symbol == '+' || symbol == '-' || symbol == '*' || symbol == '/'){

int first, second;

first = pop();

49

second = pop();

switch(symbol){

case '+':

sum = first + second;

break;

case '-':

sum = second - first;

break;

case '*':

sum = second * first;

break;

case '/':

sum = second / first;

break;

}

push(sum);

}

i++;

}

return(top());

}

50

/* PREVODJENJE IZRAZA IZ INFIKS U POSTFIKS FORMU */

#include <stdio.h>

#include <stdlib.h>

#include <io.h>

#include <string.h>

int Priority(char Operator) {

if(Operator=='*'||Operator=='/') return 2;

else if(Operator=='+'||Operator=='-') return 1;

else return 0;}

char *Compile(char *Expr) {

static char *Error="NEISPRAVAN IZRAZ";

static char Comp[256]="";

char Last=0,Ch,Ch1,Stack[256]="";

int FindFlag,VarFlag=0,SSPtr=0,CompLen=0,SynError;

int I,Parenths=0,Balance=0;

for(I=0;I < (int)strlen(Expr);I++) {

Ch=Expr[I]; SynError=1;

if(Ch>='a' && Ch<='z' && !VarFlag) {

VarFlag=1; SynError=0; Balance++; Comp[CompLen++]=Ch;

} else VarFlag=0;

if(Priority(Ch)) {

if(Priority(Last)||Last=='(') return Error;

while(1) {

SynError=0; Ch1=Stack[SSPtr-1];

if(!SSPtr||Ch1=='('||Priority(Ch)>Priority(Ch1)) {

Stack[SSPtr++]=Ch; break;

}

Comp[CompLen++]=Ch1; SSPtr--;

Balance-=(Ch1=='+'||Ch1=='-'||Ch1=='*'||Ch1=='/');

}

}

if(Ch=='(') {

Stack[SSPtr++]='('; SynError=0; Parenths++;

}

if(Ch==')'&&!(SynError=--Parenths<0))

while((Ch1=Stack[--SSPtr])!='(') {

Comp[CompLen++]=Ch1;

Balance-=(Ch1=='+'||Ch1=='-'||Ch1=='*'||Ch1=='/');

}

if(SynError) return Error;

Last=Ch;

}

if(Parenths||SynError) return Error;

for(I=SSPtr-1;I>=0;I--) {

Ch=Comp[CompLen++]=Stack[I];

Balance-=(Ch=='+'||Ch=='-'||Ch=='*'||Ch=='/');

}

if(Balance!=1) return Error;

Comp[CompLen]=0;

return Comp;

}

int main(void) {

char Str[256];

//ifstream InFile("RPN.IN"); ofstream OutFile("RPN.OUT");

//InFile>>Str; OutFile<<Compile(Str);

FILE *f,*g;

f=fopen("rpn.in","r");

g=fopen("rpn.out","w");

fscanf(f,"%s",Str);

fprintf(g,"%s",Compile(Str));

fclose(f);fclose(g);

return 0;}

51

2.4.2. 7BPrimena steka – rekurzija

Prilikom rekurzije moraju se poštovati dva osnovna principa:

1) Svaki sledeći poziv mora voditi ka konačnom rešenju problema

2) Mora postojati uslov za završetak procesa

#define DIM 1000

int faktorijelRekurzivno(int n){

int retv,pom;

if(n==1)

retv=1;

else{

pom=faktorijelRekurzivno(n-1);

retv=n*pom;

}

return retv;

}

int faktorijelNerekurzivno(int n){

int top=-1, stn[DIM],stretv[DIM],stadr[DIM],stpom[DIM];

int retv,adr,pom;

step1:

if(n==1){

retv=1;

stretv[top]=retv;

}

else{

step2:

//prevodjenje poziva faktorijelRekurzivno(n-1)

top++;

stn[top] = n;

stretv[top] = retv;

stpom[top] = pom;

stadr[top] = 3;

//postavi paramertre na nove vrednosti

n--;

goto step1;

step3:

pom = retv;

retv = n*pom;

stretv[top]=retv;

}

//prevodjenje return

if(top == -1)

return retv;

else{

n = stn[top];

retv = stretv[top];

pom = stpom[top];

adr = stadr[top];

top--;

if(adr == 3)

goto step3;

//posto je samo jedan rekurzivan poziv

//jedno je i mesto odakle se posle pozivna nastavlja funkcija

52

//goto step3

}

}

void move(int d, char f, char t)

{

/* move disk d from peg f to peg t */;

printf("moving disk %d : %c --> %c\n", d, f, t);

}

void hanoi(int h, char f, char t, char r)

{

if (h > 0)

{

hanoi(h-1, f, r, t);

move (h, f, t);

hanoi(h-1, r, t, f);

}

}

void hanoiNerekurzivno(int n, char poc, char pom, char kraj){

int stn[DIM], stpoc[DIM], stpom[DIM], stkraj[DIM], stadr[DIM];

int top=-1, adr, tmp;

step1:

if(n==1){

printf("\n %c -> %c ", poc, kraj);

goto step5;

}

//prevodjenje poziva hanoi(n-1,poc,kraj,pom);

top++;

stn[top] = n;

stpoc[top] = poc;

stpom[top] = pom;

stkraj[top] = kraj;

stadr[top] = 3;

//setuj parametre na nove vrednosti

n--;

poc = poc;

tmp = pom;

pom = kraj;

kraj = tmp;

goto step1;

step3:

printf("\n %c -> %c ", poc, kraj);

//prevodjenje poziva hanoi(n-1,pom,poc,kraj)

top++;

stn[top] = n;

stpoc[top] = poc;

stpom[top] = pom;

stkraj[top] = kraj;

stadr[top] = 5;

//setuj parametre na nove vrednosti

n--;

tmp = poc;

poc = pom;

pom = tmp;

kraj = kraj;

goto step1;

step5:

if(top == -1)

return;

53

else{

n = stn[top];

poc = stpoc[top];

pom = stpom[top];

kraj = stkraj[top];

adr = stadr[top];

top--;

if(adr == 3)

goto step3;

if(adr == 5)

goto step5;

}

}

2.5. 3BRED

Kod steka se sva umetanja i brisanja izvršavaju na kraju koji se naziva vrh (TOP), dok se kod reda sva

umetanja izvršavaju na kraju – KRAJ, a sva brisanja na drugom kraju reda, tj. početku – Čelo.

Redovi su poznati kao FIFO memorije. Red S možemo implementirati kao konačan niz S[0..n-1] i pri

tome se koriste dva podatka, CELO i KRAJ da bi se oznčile granice reda. Ako je CELO < KRAJ tada se red

sastoji od S[CELO] ... S[KRAJ-1]. U suprotnom, ako je CELO > KRAJ, tada se red sastoji od

S[CELO],...,S[n-1],S[0],...,S[KRAJ-1], i ako je CELO = KRAJ, tada je red prazan.

54

55

/* Implementacija reda uz pomoc niza */ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <stdlib.h> #define MAX 10 /* maksimalna duzina reda */ void insert(int queue[], int *rear, int value) { if (*rear < MAX - 1) { *rear = *rear + 1; queue[*rear] = value; } else { printf("Red je pun i ne moze se dodati nova vrednost\n"); exit(0); } } void delete(int queue[], int *front, int rear, int *value) { if (*front == rear) { printf("Red je prazan i ne mozemo brisati vrednost\n"); exit(0); } *front = *front + 1; *value = queue[*front]; } void print(int queue[], int front, int rear) { int i; printf("\n"); for (i = front + 1; i <= rear; i++)

56

printf("%d -> ", queue[i]); printf("KRAJ"); } void main() { int queue[MAX]; int front, rear; int value, kraj = 0, izbor; front = rear = -1; while (!kraj) { printf("\n1. Dodavanje elementa u red\n" "2. Brisanje elementa iz reda\n" "3. Stampanje reda\n" "0. Zavrsetak rada\n\n" "Vas izbor? " ); scanf("%d", &izbor); switch (izbor) { case 1: printf("\nBroj? "); scanf("%d", &value); insert(queue, &rear, value); break; case 2: delete(queue, &front, rear, &value); printf("\nPreuzeti element sa reda je %d\n", value); break; case 3: print(queue, front, rear); break; case 0: kraj = 1; break; } } } /* Implementacija reda uz pomoc linearnih listi */ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <stdlib.h> struct node { int data; struct node *link; }; void insert(struct node **front, struct node **rear, int value) { struct node *temp; temp = (struct node*) malloc(sizeof(struct node)); if (temp == NULL) { printf("No Memory available Error\n"); exit(0); } temp->data = value; temp->link = NULL; if (*rear == NULL) { *rear = temp; *front = *rear;

57

} else { (*rear)->link = temp; *rear = temp; } } void delete(struct node **front, struct node **rear, int *value) { struct node *temp; if ((*front == *rear) && (*rear == NULL)) { printf("Red je prazan nema brisanja!\n"); exit(0); } *value = (*front)->data; temp = *front; *front = (*front)->link; if (*rear == temp) *rear = (*rear)->link; free(temp); } void print(struct node *front, struct node *rear) { struct node *temp = front; printf("\n"); while (temp) { printf("%d -> ", temp->data); temp = temp->link; } printf("KRAJ"); } void main() { struct node *front = NULL, *rear = NULL; int value, kraj = 0, izbor; while (!kraj) { printf("\n1. Dodavanje elementa u red\n" "2. Brisanje elementa iz reda\n" "3. Stampanje reda\n" "0. Zavrsetak rada\n\n" "Vas izbor? " ); scanf("%d", &izbor); switch (izbor) { case 1: printf("\nBroj? "); scanf("%d", &value); insert(&front, &rear, value); break; case 2: delete(&front, &rear, &value); printf("\nPreuzeti element sa reda je %d\n", value); break; case 3: print(front, rear); break; case 0: kraj = 1; break; } } }

58

2.6. NELINEARNE STRUKTURE PODATAKA

Najznačajnije nelinearne strukture podataka su stabla i grafovi.

2.7. STABLA

2.7.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 acikličan, orijentisan graf koji ima jedan čvor koji se zove koren (root) sa ulaznim stepenom 0

dok svi drugi čvorovi imaju ulazni stepen 1. Ako izbrišemo koren i njegove grane dobijamo skup disjunktnih

stabala. Svaki čvor koji ima izlazni stepen 0 naziva se terminalni čvor ili list, dok se svi drugi čvorovi

nazivaju čvorovi grananja (brunch nodes).

• Nivo čvora je dužina puta od korena, d.

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

• Za stablo se kaže da je n-arno (reda n) ako svaki čvor ima najviše n podčvorova.

• Za stablo se kaže da je puno ako se svi listovi nalaze na istom rastojanju od korena, tj. ako od

korena do svakog lista odgovara put dužine h-1.

• Za stablo se kaže 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 može smestiti u čvor.

• Za stablo se kaže da je balansirano ako za svaki čvor važi da se broj čvorova u svakom

njegovom podstablu ne razlikuje za više od 1.

• Za stablo reda n čiji su svi čvorovi na nivoima od 1 do h-1 kompletni, kaže se da je optimalno

balansirano.

2.7.2. OPERACIJE NA BINARNIM STABLIMA

Neke od operacija nad binarnim stablom su: prolaz, umetanje, brisanje, pretraživanje i kopiranje.

59

/* 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); /* Stavaranje praznog stabla. */

int vel (Stablo koren); /* Broj cvorova u stablu. */

int zbir (Stablo koren); /* Zbir brojeva u stablu. */

void pisi_kld (Stablo koren); /* Prefiksno ispisivanje. */

void pisi_lkd (Stablo koren); /* Infiksno ispisivanje. */

void pisi_ldk (Stablo koren); /* Postfiksno ispisivanje. */

void crtaj (Stablo koren, int nivo); /* Graficki prikaz stabla. */

int pojav (Stablo koren, int b); /* Broj pojavljivanja u stablu. */

int min_u (Stablo koren); /* Najmanji u uredjenom stablu. */

int max_u (Stablo koren); /* Najveci u uredjenom stablu. */

int min_n (Stablo koren); /* Najmanji u neuredjenom stablu. */

int max_n (Stablo koren); /* Najveci u neuredjenom stablu. */

int uredjeno (Stablo koren); /* Da li je stablo uredjeno? */

Cvor *nadji_u (Stablo koren, int b); /* Trazenje u uredjenom stablu. */

Cvor *nadji_n (Stablo koren, int b); /* Trazenje u neuredjenom stablu. */

Stablo dodaj_u (Stablo koren, int b); /* Dodavanje u uredjeno stablo. */

Stablo dodaj_n (Stablo koren, int b); /* Dodavanje u neuredjeno stablo. */

Stablo citaj_u (int n); /* Citanje uredjenog 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);

60

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: */

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: */

printf ("Zbir= %d\n", zbir (koren)); break;

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: */

61

koren = brisi (koren); break;

case '0': /* Zavrsetak rada: */

kraj = 1; break;

default: /* Pogresan izbor: */

printf ("*** Nedozvoljeni izbor! ***\a\n"); break;

}

}

}

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; }

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; }

62

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 *nadji_u (Stablo koren, int b) { /* Trazenje u uredjenom stablu. */

if (! koren) return NULL;

if (koren->broj == b) return koren;

if (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);

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); }

63

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;

}

}

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);

}

64

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);

}

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 reči (koje su zapisane u posebnim redovima datoteke) od kojih ni jedna nije

dužine veće od 30 karaktera. Ispisati na standardni izlaz samo različite reči sortirane leksikografski. Uz svaku

reč ispisati i broj pojava. Kraj unosa je marker kraja (EOF). Smatrati da je reč niska sastavljen isključivo od

slova i cifara i broj pojava svake reči nije veći 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 *);

65

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 */

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;

66

s[++i] = '\0'; /* zavrsiti nisku */

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 aritmetički izraz zapisan u prefksnoj notaciji

operator izraz1 izraz2, smešta ga u niz karaktera dužine 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 učitanog stringa formira binarno stablo. Prilikom zadavanja izraza očekujemo 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

računa vrednost izraza koji se nalazi u stablu.

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

#include <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){

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

exit(1);

67

}

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 */

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;

}

}

68

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) +

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

69

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);

}

70

2.8. GRAFOVI

Definicija: Graf G je uređeni par G=(V,E) gde je EVxV. Skup V je skup čvorova, dok skup E

predstavlja skup grana (veza između čvorova).

• Grane usmerenog grafa su uređeni parovi čvorova i redosled dva čvora koje povezuje grana je bitan.

• Za neusmeren graf važi da ukoliko je čvor u u vezi sa čvorom v, onda je i čvor v u vezi sa čvorom u.

• Težinski graf je graf čijim granama su pridruženi jedan ili više realnih brojeva ( kao vrednost rastojanja, težina, cene,...).

usmeren graf neusmeren graf težinski 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 početak).

• Bipartitni graf je graf čiji se čvorovi mogu podeliti na dva disjunktna podskupa tako da u grafu

postoje samo grane između čvorova iz različitih 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 tačno jednom.

NAPOMENA: Sve date definicije su opisne. Za precizne definicije matematičkih pojmova vezanih za

grafove konsultovati literaturu.

2.8.1. Predstavljanje grafova u memoriji računara

Predstavljanje uz pomoć matrice susedstva

Jedan način 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 između č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

71

neorijentisan graf. Ako je G usmeren graf, samo e elemenata matrice će biti 1. Primeri predstavljanja

grafova uz pomoć matrice susedstva su dati na sledećim slikama:

Graf Matrica susedstva Ulazni i izlazni stepeni čvora

Predstavljanje grafova uz pomoć povezanih listi

Drugi način predstavljanja grafa G je uz pomoć povezanih listi. Za svaki čvor konsturišemo povezanu

listu koja sadrži sve čvorove susedne datom čvoru.

72

2.8.2. Računanje ulaznog i izlaznog stepena čvora grafa predstavljenog pomoću 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);

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));

}

}

2.8.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 „otkrivajući“ svaki čvor koji je dostižan iz s. Istovremeno,

možemo računati i rastojanje čvora s do svakog čvora do kojeg postoji put iz s. Izvršenjem algoritma

možemo konstruisati tzv. BFS stablo koje će sadržati sve čvorove grafa G dostižne iz s. Algoritam se

može primeniti i na orijentisane i neorijentisane grafove. Na sledećoj slici ilustrovano je izvršenje BFS

algoritma za dati graf G i startni čvor s.

73

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

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

2.8.4. Obilazak grafa u dubinu (DFS)

Strategija koju koristi DFS algoritam je tražiti čvorove sve „dublje“ u grafu kad god je to moguće. Kod

DFS algoritma pretražujemo granu koja napušta čvor v i a zatim i sve naredne grane. Kada završimo sa

pretragom svih grana koje napuštaju čvor v vraćamo se unazad da bi smo nastavili sa pretragom grana

koje napuštaju čvor iz kojeg smo stigli u čvor v.

74

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)

color[u] ← GRAY White vertex u has just been discovered.

time ← time +1 d[u] <- time for each v in 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]);

75

}

}

/* 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*/

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;

76

*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);

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);

}

2.8.5. Topološko sortiranje

Topološko sortiranje usmerenog acikličnog grafa G=(V,E) predstavlja linearno uređenje svih čvorova

tako da ukoliko graf G sadrži granu (u,v), tada se čvor u nalazi pre čvora v u dobijenom nizu čvorova.

Ukoliko graf nije acikličan, topološko sortiranje je nemoguće. Usmeren acikličan graf se veoma često koristi

za predstavljanje veze između događaja. Grana (u,v) znači da se događaj u mora završiti pre nego što

počne izvršenje događaja v.

77

Na slici je dat primer grafa koji predstavlja redosled oblačenja delova odeće i topološko sortiranje

datog grafa (slika (b)).

Algoritam topološkog sortiranje koristi DFS algoritam i može 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 topološki 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) );

ptr->count = v;

ptr->next = zerolist;

zerolist = ptr;

}

node *buildGraph(char *fileName, int *n) {

/* ucitavamo graf iz datoteke

78

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 )

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;

79

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 );

}

}

int main() {

int n;

buildGraph("mat.txt",&n);

printGraph(graph,n);

topsort(n);

}

Objašnjenje

Digraf G je predstavljen uz pomoć povezanih listi. U ovakvoj reprezentaciji G je niz graph[0…n–1],

gde je svaki element graph[i] povezana lista čvorova grafa sa kojima je čvor i povezan, a n je broj

čvorova u grafu G.

Promenljiva zerolist služi da bismo čuvali listu čvorova koji nemaju prethodnike.

Algoritam topsort() se služi 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ć

obiđeni. Svi čvorovi u zerolist su potencijalni kandidati za sledeći čvor koji će biti odštampan, tj.

80

ubačen u sortirani niz. Nakon što smo odštampali v svi čvorovi sa kojima je on u vezi mogu postati

kandidati za sledeći odštampani čvor.

Primer: Analiza algoritma na sledećem grafu

Korak Zerolist Izlaz

0 {0} nil

1 {1, 2, 3} 0

2 {2, 3} 1

3 {3} 2

4 {4,5} 3

5 {5} 4

6 {} 5

2.8.6. Jako povezane komponente grafa

Povezana komponenta grafa je maksimalni podgraf dataog grafa koji je povezan. Razmotrimo sledeći

graf.

Povezana komponenta ovog grafa je:

81

Jako povezane komponente

Za digraf G, jako povezana komponenta je podgraf grafa G u kojem za svaki par čvorova (u,v) važi da

postoji put i od u do v i od čvora v do čvora u. Posmatrajmo graf na sledećoj slici.

Jako povezane komponente datog grafa su:

82

#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;

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 ) {

83

/*

* 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 );

printGraph( graph, nvert );

compINC( graph, nvert );

fclose(f);

return 0;

}

Graf je predstavljen uz pomoć povezanih listi. Graf je predstavljen kao niz od n pokazivača gde n

predstavlja broj čvorova grafa. Svaki član niza i sadrži pokazivač na povezanu listu čvorova sa kojima je

čvor i u vezi. Na primer, sledeći graf:

84

se predstavlja sledećom strukturom:

2.8.7. Minimum Spanning Tree (minimalno razapinjuće stablo)

Definicija: Neka je dat povezan, neorijentisani graf G = (V, E) sa težinskom funkcijom w : E → R.

Minimalno razapinjuće stablo grafa G je podgraf (V,T), gde je T ⊆ E, koji povezuje sve čvorove grafa G i

čija je ukupna težina ∑ 𝑤(𝑢, 𝑣)(𝑢,𝑣)𝜖𝑇 minimalna.

Pretpostavimo da imamo povezan, neorijentisani graf G = (V, E) sa težinskom funkcijom w : E → R, i

želimo da nadjemo minimalno razapinjuće stablo za graf G. Dva algoritma koja ćemo predstaviti u osnovi

imaju istu strategiju nalaženja minimalnog razapinju+ćeg stabla. Naime, oba polaze od skupa grana A koji

je na početku 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 razapinjuće stablo (znači imamo n-1 koraka). U

svakom koraku A je podskup minimalnog razapinjućeg 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 ← Ø

85

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 manipuliše sa supergrafom koji je

indukovan polaznim grafom. Ako je polazni graf G=(V,E), onda je supergraf uređeni par SG=(SV,SE),

takav da je:

• SV skup čvorova;

• SE skup superivica;

• svaki superčvor sv je podskup skupa čvorova V;

• svaka dva superčvora su disjunktna;

• unija svih superčvorova je skup V;

• između superčvorova su i sv postoji ivica ako i samo ako postoji čvor u su i čvor v sv, tako

da je (u,v)E.

• dužina superivice (su,sv) je dužina najkraće ivice grafa (G,E) čiji je jedan kraj u superčvoru su, a drugi u superčvoru sv.

Na početku izvršenja Kruskalovog algoritma superčvorovi su jednočlani, tj. svakom čvoru u polaznog

grafa odgovara superčvor su={u}, a Eπ je prazan. Algoritam je iterativan. U svakoj iteraciji SE izvršava

sledeći niz radnji:

• dva superčvora se spajaju u jedan, čime se broj superčvorova smanjuje;

• jedna ivica se dodaje minimalnom drvetu (skupu Eπ).

Pri tome:

• spajaju se najbliži superčvorovi (superčvorovi između kojih je najmanje rastojanje);

• određuje se najkraća ivica između čvorova koji su sadržani u paru prethodno određenih superčvorova i ta ivica se dodaje drvetu.

Petlja se ponavlja dok ne dobijemo supergraf sa samo jednim superčvorom. Kako je na početku

ukupno |V| superčvorova, izvršiće 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

*/

86

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;

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:");

87

for(i=0;i<nvert-1;i++)

printf("\n%d -> %d",v1[i],v2[i]);

printf("\n*************** kraj ****************\n");

return 0;

}

Isti algoritam se može modifikovati tako da postane efikasniji. Naime, u svakom prolazu kroz

spoljašnju petlju se određuje najkraća ivica čiji su krajevi u različitim superčvorovima. Ako bi se ivice

sortirale pre početka izvršavanja te petlje, onda se računanje pojednostavljuje. Tada bismo u petlji

pronalazili sledeću ivicu čiji su krajevi u različitim superčvorovima, i dodavali bismo je drvetu.

88

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 generiše tako što se kreće od proizvoljnog čvora koji će biti koren

drveta, i dodaje jedan po jedan čvor. Tako da u toku izvršenja 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 početku se skup U sastoji samo od jednog čvora – izabrani koren drveta. U svakoj iteraciji se bira

nakraća 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π).

Izbor najkrće ivice sa zadanim svojstvima je ekvivalentan izboru čvora iz skupa V\U koji je najbliži

skupu čvorova U. Rastojanje između čvora i skupa čvorova se definiše kao dužina najkraće ivice čiji je

jedan kraj upravo taj čvor, a drugi kraj je u skupu.

Da bismo smanjili računanje, nakon dodavanja novog čvora (u) u skup U, za sve čvorove iz skupa V\U

korigujemo najkraće rastojanje do skupa U. Korekciju izvodimo po formuli:

89

d(v,U{u}) = min {d(v,U),e(u,v)},

gde je e(u,v) dužina 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, izvršava se ukupno |V|-1 iteracija. Funkcija se može zapisati na sledeći način: (glavni

program je identičan 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))

if((dm[j] == 0) || (dm[j] > e[k][j])){

vp[j]=k;

dm[j]=e[k][j];

}

}

return 0;

}

90

2.8.8. Nalaženje najkraćeg 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)

91

3 π[v] ← u

Bellman – Ford algoritam

Bellman-Ford algoritam nalazi najkraće rastojanje od datog čvora do svih ostlih čvorova grafa čak i za

slučaj kada postoje ivice sa negativnom dužinom. Algoritam otkriva postojanje negativnih petlji i u tom

slučaju daje odgovarajući izveštaj, odnosno ne određuje rastojanja.

Polazna ideja je da svaki put, pa i najkraći između bilo koja dva čvora, može imati najviše |V| -1 ivicu.

Tako se izvršava |V| -1 prolaz kroz petlju u kojoj se proverava za svaku ivicu da li smanjuje najkraće

rastojanje između polaznog čvora i nekog od preostalih čvorova.

Nakon toga još jednom ponovimo navedeni postupak za svaku ivicu. Ako se tim prolazom skrati neko

od izračunatih rastojanja, u grafu postoji petlja negativne dužine do koje se može stići od polaznog čvora

(s) i određivanje najkraćih 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 izvršenja ovog algoritma.

92

#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

}

}

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:

93

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++)

printf("\nRastojanje od cvora %d do cvora %d iznosi: %d",vs,i,d[i]);

printf("\n*************** kraj ****************\n");

return 0;

}

Dijkstrin algoritam za nalaženje najkraćih puteva

U svakoj iteraciji ovog algoritma se određuje najkraće rastojanje od početnog čvora (s) do jednog od

preostalih čvorova. U toku izvršavanja algoritma manipuliše se skupom čvorova U za koje je izračunato

najkraće rastojanje od čvora s.

Na početku 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 određuje se najkraći put (dužina najkraćeg puta) od

94

č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 najkraći.

Dužine puteva se određuju na početku (kada je U={s}), posle čega se koriguje pri dodavanju novog

elementa u skup U. Na početku je dužina puta jednaka:

➢ dužini ivice između čvora s i odgovarajućeg č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, računati put se može 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 dužinu jednaku zbiru dužine puta od čvora s do u i

dužinu ivice od čvora u do čvora v (ako ta ivica postoji). Najkraći put je jednak minimumu dosadašnje

dužine 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;

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]))

95

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;

}

96

2.8.9. Najkraće rastojanje između svih parova čvorova (Floyd-Warshall)

Alogoritam Floyd-Warshall – a podrazumeva iterativni postupak za određivanje najkraćih rastojanja

između svih parova čvorova.

Na početku čvorove numerišemo brojevima od 0 do n-1. Neka je d(k)i,j najkraći put između č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

dužini ivice između čvorova i i j, ako postoji. Takođe je d(n-1)i,j jednako upravo najkraćem putu između

čvorova i i j.

Računanje vrednosti d(k+1)i,j je prilično jednostavno. Može se pokazati da je:

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

i,j , d(k)

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

Program za računanje najkraćih 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;

97

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);

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;

}

98

3. Sortiranje

3.1. Sortiranje izborom uzastоpnih minimuma (selection-sort)

Postupak biranja najmanjeg elementa u nizu:

- na početku algoritma uzima se da je najmanji element – prvi element.

- svaki sledeći element se poredi sa prvim i ukoliko je tekući element manji od prvog, zamenjujemo

mesta ovim elementima u nizu.

Niz se sortira tako što se pronađe najmanji element celog niza, pa najmanji element od preostalog

dela niza (svih elementa sem prvog) itd.

Funkcija za sortiranje niza se može napisati:

void ssort (int n, int a[]){

int i,j,k;

for(i=0;i<n-1;i++)

for(j=i+1;j<n;j++)

if(a[i]>a[j]){

k=a[i];

a[i]=a[j];

a[j]=k;

}

}

U navedenoj funkciji svaka razmena zahteva tri dodeljivanja. Zato je efikasnije da izračunamo indeks

najmanjeg elementa dela niza i tek nakon toga razmenimo vrednosti i-tog elementa niza i najmanjeg

elementa niza. Tako dobijamo funkciju:

void ssort2(int n, int a[]){

int i,j,k;

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

for(k=i,j=i+1;j<n;j++)

if(a[k]>a[j]) k=j;

if(i!=k){

j=a[k];a[k]=a[i];a[i]=j;

}

}

}

3.2. Sortiranje umetanjem elemenata niza na odgovarajuća mesta (insertion sort)

Algoritam se sastoji u sledećem:

- dodati prvi element u niz. Možemo smatrati da je jednoelementni niz sortiran.

- Pretpostavimo da smo od prvih k elemenata niza napravili sortiran niz. Dodajemo k+1 element

niza tako da posle toga niz i dalje bude sortiran. Novi element se dodaje na kraj sortiranog niza.

Taj element se poredi sa prethodnim elementom (ako prethodni postoji) i, ako je manji, zamenjuju

99

im se mesta. Postupak poređenja i zamene se ponavlja sve dok postoji prethodni element datom

elementu u nizu i dok je on veći.

void isort(int n, int a[]){

int i,j,k;

for(i=1;i<n;i++)

for(j=i;(j>0) && (a[j]<a[j-1]);j--){

k=a[j];a[j]=a[j-1];a[j-1]=k;

}

}

3.3. Sortiranje poređenjem parova uzastopnih elemenata (bubble-sort)

Porede se svaka dva elementa niza, počev od prvog elementa. Ako je prethodnik veći od sledbenika

onda ta dva elementa razmenjuju vrednosti. Kada se na takav način prođe kroz ceo niz, poslednji element

niza će biti najveći. Postupak se nastavlja upoređivanjem parova elemenata, sem poslednjeg para jer je

poslednji elemenat najveći.

Poređenja se mogu vršiti od poslednjeg para, i na taj način dobijamo na prvom mestu najmanji

element.

Pored toga, može se desiti da u jednom izvršavanju spoljašnje petlje (izvršava se i poređenja) ne

obavimo nijednu zamenu vrednosti para elemenata. To će značiti da je niz sortiran i da dalje sortiranje

treba prekinuti.

int bsort (int n, int a[]){

int i,j,k,l;

l=1;

for(i=n-1;l && (i>0);i--)

for(j=l=0;j<i;j++)

if(a[j]>a[j+1]){

k=a[j];

a[j]=a[j+1];

a[j+1]=k;

l=1;

}

}

3.4. Sortiranje korišćenjem stabla (heap-sort)

Sortiranje korišćenjem drveta (heap-sort) je postupak sortiranja putem binarnog drveta. U toku

sortiranja se formira binarno drvo za koje važi:

(i) Svi čvorovi, osim čvorova na pretposlednjem nivou, imaju oba naslednika.

(ii) Određen broj krajnje levih čvorova na pretposlednjem nivou ima oba naslednika, zatim jedan

čvor može imati samo levog naslednika (postoji najviše jedan čvor sa samo jednim i to

obavezno levim naslednikom) i preostali čvorovi nemaju naslednika.

100

(iii) Svim čvorovima u drvetu su pridružene vrednosti iz niza koji sortiramo i pri tome je vrednost

pridružena proizvoljnom čvoru veća (ili jednaka) od vrednosti pridruženih njegovim

naslednicima.

3.5. Sortiranje deljenjem niza (quick sort)

Postupak sortiranja ovim algoritmom se sastoji iz dva dela. U prvom delu niz se deli na dva dela.

Jedan deo čine svi elementi koji su manji ili jednaki nekoj unapred odabranoj vrednosti (obično je to neki

element niza: prvi, poslednji ili srednji).

Drugi deo čine svi elementi veći od te iste vrednosti. Mi ovde izvodimo deljenje u odnosu na prvi

element niza.

Deljenje se obavlja tako što uzimamo jedan po jedan element niza i dodajemo odgovarajućem delu.

Tako u proizvoljnom trenutku možemo izdvojiti tri dela miza

- elemente koje još nismo obradili (od i+1-og do poslednjeg)

- elemente koji su obrađeni i čine grupu manjih ili jednakih elemenata niza (od nultog do j-tog)

- elemente koji su obrađeni i čine grupu većih elemenata niza (od j+1-og do i-tog člana niza).

Ako sledeći element (i+1-vi) pripada grupi manjih ili jednakih, prvi element iz grupe većih (j+1-vi u nizu)

zamenjuje mesto sa analiziranim (i+1). Tako je i+1-vi element dodat grupi manjih ili jednakih i grupa manjih

je uvećana za jedan element (povećava se vrednost promenljive j).

Ako sledeći element pripada grupi većih, tada se samo grupa većih uvećava za jedan element, dok grupa

manjih ostaje ista (ne menja se vrednost promenljive j).

Nakon obrade svih elemenata, niz se sastoji iz samo dva dela: grupa manjih (ili jednakih) i grupa većih

(grupa elemenata koji nisu obrađeni je postala prazna). Potrebno je još nultom i j-tom elementu zameniti

mesta. Tako će na kraju svi elementi levo od j-tog biti manji (ili jednaki) od njega, a svi desno veći.

# include <stdlib.h> # define MAXEL 1000 int zameni (int ae[], int i, int j) { int k; k = ae[i]; ae[i] = ae[j]; ae[j] = k; } int podeli (int an1, int an2, int ae[]) { int i, j; for (j = an1, i = an1+1; i <= an2; i++) if (ae[i] <= ae[an1]) zameni (ae, ++j, i); zameni (ae, an1, j); return(j); }

101

int quick1 (int an1, int an2, int ae[]) { int i, j; if (an1>=an2) return(0); if (an1+1==an2) if (ae[an1] > ae[an2]) { zameni (ae, an1, an2); return (0); } i = podeli (an1, an2, ae); quick1 (an1, i-1, ae); quick1 (i+1, an2, ae); } int quick (int an, int ae[]) { quick1 (0, an-1, ae); } int prn_niz (int an, int ae[]) { int i; for (i = 0; i < an; i++) printf ("%d\n", ae[i]); } int main () { int an, i; int ae[MAXEL]; printf ("n = "); scanf ("%d", &an); for (i = 0; i < an; i++) ae[i] = rand(); quick (an, ae); prn_niz (an, ae); }

102

4. Zadaci za vežbu

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 veći

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 vraća labelu onog lista binarnog stabla koji ima najmanji nivo. Ukoliko postoji više

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 „slična kao u ogledalu“ ako su oba prazna ili ako nisu prazna, ako je levo stablo svakog stabla „slično kao u ogledalu“ desnom stablu onog drugog. Na sledećoj slici su prikazana dva „slična kao u ogledalu“ stabla:

Napišite funkciju koja će proveriti da li su dva binarna stabla „slična kao u ogledalu“. /* Struktura binarnog stabla*/

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

typedef Cvor *Stablo;

6. Rekonstruišite binarno stablo čiji čvorovi imaju imena A, B, . . ., J ako je poznato da INORDER obilazak ispisuje čvorove u redosledu GDJEAICHBF, a POSTORDER

103

obilazak u redosledu DJGIABHFCE. Detaljno obrazložite svaki korak. Nakon rekonstrukcije ispišite 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. Kažemo da je čvor p binarnog stabla zajednički predak čvorova u i v ako je p istovremeno predak čvora u i predak čvora v. Čvor np je najbliži predak od u i v ako od svih njihovih zajedničkih predaka upravo np ima najveću visinu. Napišite funkciju node np(BTREE T, node u, node v) koja vraća ime najbližeg pretka čvorova u i v. U primeru desno, čvorovi a i c su zajednički pretci čvorova f i e - ali kako c ima veću visinu, on je najbliži predak.

8. Udaljenost čvorova u i v računamo kao broj grana koji treba preći na putu koji spaja u i v. Napišite funkciju int udalj(BTREE T, node u, node v) koja vraća udaljenost čvorova u i v u stablu T. Možete koristiti pozive funkcije iz (a). U primeru desno, dužina puta koji spaja f i e je jednaka 3.

9. Kažemo 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 veći od nivoa čvora u. Tako su npr. deca nekog čvora njegovi 1-potomci, deca od dece 2-potomci, itd. Napišite funkciju sa prototipom labeltype potomak(BTREE T,

int k) koja vraća label onog ćvora bin. stabla T koji ima najviše k-potomaka. Ako ima više takvih čvorova, vratite labelu bilo kojeg. Funkcija treba biti neovisna o implementaciji atp-a BTREE; ne smete koristiti pomoćne atp-ove. Možete definisati pomoćne 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.

10. Napišite funkciju sa prototipom labeltype najvisi_list(BTREE T) koja

vraća labelu onog lista binarnog stabla T koji ima najmanji nivo. Ako ima više 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

104

ovaj primer treba vratiti b.

11. Rekonstruišite 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 obrazložite svaki korak. Nakon rekonstrukcije ispišite i PREORDER obilazak dobivenog stabla.

12. Date su dve datoteke koje u svakoj liniji sadrže po jednu nisku sa ne više od 80 karaktera. Napisati program koji na standardni izlaz ispisuje sve niske prve datoteke koje nisu sadržane u drugoj datoteci. Zadatak realizovati korišćenjem binarnog stabla pretrage.

13. U binarno stablo smeštaju se celobrojni podaci (int) u intervalu [1, X] (gdje X prestavlja zbir cifara vašeg matičnog broja - JMBAG). Napisati funkciju koja će u stablo dodati novi element (ali samo ako on u stablu već ne postoji). Napisati drugu funkciju koja će elemente zadanog stabla ispisati na ekran od najmanjeg prema najvećem. Napisati glavni program u kojem slučajno generirati 20 brojeva u intervalu [1, X]. Koristeći prvu napisanu funkciju, tako dobivene brojeve upisati u binarno stablo. Pomoću druge napisane funkcije ispisati sve elemente stabla. Odrediti apriornu složenost za obje funkcije i glavni program.

14. U binarno stablo smeštaju se celobrojni podaci (int) u intervalu [1, X] (gde X prestavlja zbir cifara vašeg matičnog broja - JMBG). Napisati funkciju koja će u stablo dodati novi element (ali samo ako on u stablu već ne postoji). Napisati drugu funkciju koja će za zadato stablo izračunati najveću dubinu. Napisati glavni program u kojem slučajno generisati 20 brojeva u intervalu [1, X]. Koristeći prvu napisanu funkciju, tako dobijene brojeve upisati u binarno stablo. Pomoću druge napisane funkcije izračunati i ispisati najveću dubinu stabla.

15. U binarno stablo smeštaju se celobrojni podaci (int) u intervalu [1, X] (gdje X prestavlja zbroj cifara vašeg matičnog broja - JMBAG). Napisati funkciju koja će u stablo dodati novi element (ali samo ako on u stablu već ne postoji). Napisati drugu funkciju koja će za zadato stablo izračunati prosek svih elemenata. Napisati glavni program u kojem slučajno generisati 20 brojeva u intervalu [1, X]. Koristeći prvu napisanu funkciju, tako dobijene brojeve upisati u binarno stablo. Pomoću druge napisane funkcije izračunati i ispisati prosek elemenata stabla.

16. U binarno stablo smeštaju se celobrojni podaci (int) u intervalu [1, X] (gdje X prestavlja zbir cifara vašeg matičnog broja - JMBAG). Napisati funkciju koja će u stablo dodati novi element (ali samo ako on u stablu već ne postoji). Napisati drugu funkciju koja će za zadato stablo ispisati sve elemente koji se nalaze na zadatom nivou (dubini). Napisati glavni program u kojem slučajno generisati 20 brojeva u

105

intervalu [1, X]. Koristeći prvu napisanu funkciju, tako dobijene brojeve upisati u binarno stablo. Pomoću druge napisane funkcije za prvih 10 nivoa stabla (od korena prema listovima) ispisati elemente na svakom nivou.

17. U binarno stablo upisuju se cjelobrojni podaci (int) u intervalu [1, X] (gdje X prestavlja zbir cifara vašeg matičnog broja - JMBAG). Napisati funkciju koja će u stablo dodati novi element (ali samo ako on u stablu već ne postoji). Napisati drugu funkciju koja će za zadano stablo ispisati sve listove tog stabla. Napisati glavni program u kojem slučajno generirati 20 brojeva u intervalu [1, X]. Koristeći prvu napisanu funkciju, tako dobijene brojeve upisati u binarno stablo. Pomoću druge napisane funkcije ispisati sve listove dobijenog stabla.

18. U kvadratnu matricu dimenzije N x N (N < 100) ulazi se na gornji levi ugao, tj. polje (1,1). Može se kretati samo desno, gore (više), ili dole (niže). Prilikom stajanja na neko polje plaća se cena jednaka vrednosti zapisanoj u tom polju. Izaći preko gornjeg desnog ugla, tj. polja (1,N), prolazeći “najjeftinijim” putem. Štampati cenu tog puta i niz poteza koje treba napraviti stavljajući D, N, V za desno, niže i više. primer:

5

5. 2 1 9 8 3

6. 7 3 9 1 2

7. 8 2 2 1 5

8. 3 5 3 2 4

9. 2 4 6 8 5

izlaz: 17, DNNDDVDV Ovaj zadatak se radi primenom Dijkstra algoritma.

19. N gradova označeno je brojevima 1,2,...,N. Neki od njih povezani su direktnim putevima. Putevi nemaju raskrsnica, ako se putevi ukrštaju, prelaze nadvožnjacima i ne može se preći sa jednoga na drugi. Iz ulazne datoteke se učitavaju parovi gradova koji su direktno povezani. Odrediti na koliko se grupa uzajamno povezanih gradova raspada data grupa od N gradova. Štampati te grupe. Na putu između koja dva grada se mora proći kroz najveći broj gradova? Štampati taj put.

Primer:

ulaz: 1 3 1 5 3 5

izlaz: 1 3 5 7 2 4 6 9 8

106

3 7 2 4 4 6 6 9

3 grupe 2 4 6 9