JEDNOSTRUKO I DVOSTRUKO LISTE TEKOVIstasa/P2/Cas 11.pdf · /* Pomocna funkcija koja kreira cvor....

Preview:

Citation preview

PROGRAMIRANJE 2

VEŽBE

JEDNOSTRUKO I DVOSTRUKO

POVEZANE LISTE. STEKOVI.

Staša Vujičić Stanković

JEDNOSTRUKO POVEZANE LISTE. REKURZIVNO

#include <stdio.h>

#include <stdlib.h>

typedef struct cvor{

int vrednost;

struct cvor * sledeci;

} Cvor;

2

void prikazi_elemente(Cvor *glava){

if(glava == NULL)

return;

printf(" %d",glava->vrednost);

prikazi_elemente(glava->sledeci);

}

void prikazi_listu(Cvor* glava){

putchar('[');

prikazi_elemente(glava);

printf("]\n");

} 3

Cvor* oslobodi_listu(Cvor* glava){

if(glava == NULL)

return NULL;

oslobodi_listu(glava->sledeci);

free(glava);

return NULL;

}

4

Cvor* napravi_cvor(int x){

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

if(novi == NULL)

return NULL;

novi->vrednost = x;

novi->sledeci = NULL;

return novi;

} 5

Cvor* dodaj_na_pocetak(Cvor* glava, int x){

Cvor *novi = napravi_cvor(x);

if(novi == NULL){

fprintf(stderr,"malloc() greska u funkciji

napravi_cvor()!\n");

glava = oslobodi_listu(glava);

exit(EXIT_FAILURE);

}

/* uvezujemo ga na pocetak */

novi->sledeci = glava;

return novi;

}

6

Cvor* dodaj_na_kraj(Cvor* glava, int x){

if (glava == NULL){

Cvor* novi = napravi_cvor(x);

if(novi == NULL){

fprintf(stderr,"malloc() greska u funkciji

napravi_cvor()!\n");

glava = oslobodi_listu(glava);

exit(EXIT_FAILURE);

}

return novi;

}

glava->sledeci = dodaj_na_kraj(glava->sledeci,x);

return glava;

}

7

Cvor* dodaj_sortirano(Cvor* glava, int x){

if(glava == NULL || glava->vrednost >= x )

return dodaj_na_pocetak(glava, x);

glava->sledeci = dodaj_sortirano(glava->sledeci, x);

return glava;

}

8

Cvor* pretrazi_listu(Cvor* glava, int x){

if(glava->vrednost == x)

return glava;

return pretrazi_listu(glava->sledeci, x);

}

9

Cvor* obrisi_element(Cvor* glava, int x){

if(glava == NULL)

return NULL;

glava->sledeci = obrisi_element(glava->sledeci,x);

if(glava->vrednost == x ){

Cvor* tekuci = glava->sledeci;

free(glava);

glava = tekuci;

}

return glava;

}… test program …

10

JEDNOSTRUKO POVEZANE LISTE. ITERATIVNO

Prilikom promene liste (dodavanje novog elementa,

brisanje elementa, premeštanje elemenata, itd.)

postoji mogućnost da glava liste bude promenjena,

tj.

da to postane neki drugi čvor (sa drugom adresom).

U tom slučaju se pokazivač na glavu liste mora

ažurirati.

11

Kada promenu liste obavljamo u posebnoj funkciji

(kao što je bio slučaj u prethodnom primeru, gde

smo za dodavanje i brisanje imali posebne funkcije)

onda je potrebno da se

pozivajućoj funkciji vrati informacija o promeni

adrese glave,

kako bi pozivajuća funkcija mogla da ažurira svoju

pokazivačku promenljivu.

12

Ovo može da se uradi na dva načina:

1. Pozvana funkcija koja vrši promenu na listi

vraća kao povratnu vrednost adresu glave

nakon promene.

Ova adresa može biti ista kao i pre promene

(ako glava nije dirana) a može se i razlikovati.

U svakom slučaju, ta adresa treba da se dodeli

pokazivačkoj promenljivoj koja čuva adresu

glave u pozivajućoj funkciji.

Zbog toga se funkcija za promenu liste uvek

poziva na sledeći način:

pok = funkcija_za_promenu(pok, ...);

tj. promenljivoj čija se vrednost predaje kao

adresa glave u pozivu mora se dodeliti povratna

vrednost funkcije, za slučaj da je adresa glave

interno promenjena.

13

2. Pozvana funkcija koja vrši promenu na listi prihvata

kao argument pokazivač na pokazivačku

promenljivu koja u pozivajućoj funkciji čuva adresu

glave i koju eventalno treba ažurirati.

Sada pozvana funkcija može interno da preko

dobijenog pokazivača promeni promenljivu

pozivajuće funkcije direktno. Npr:

funkcija_za_promenu(&pok, ...);

Funkcija koja se poziva je po pravilu void tipa.

Prednost drugog metoda je jednostavnije pozivanje,

dok je prednost prvog metoda jednostavnija sintaksa

unutar pozvane funkcije. 14

JEDNOSTRUKO POVEZANE LISTE. ITERATIVNO

/* Struktura koja predstavlja cvor liste */

typedef struct cvor {

int vrednost; /* podatak koji cvor sadrzi */

struct cvor *sledeci; /* pokazivac na sledeci cvor */

} Cvor;

15

/* Pomocna funkcija koja kreira cvor.

Funkcija vrednost novog cvora inicijalizuje na broj,

dok pokazivac na sledeci cvor u novom cvoru

postavlja na NULL.

Funkcija ispisuje poruku o gresci i prekida program u

slucaju da malloc() funkcija ne uspe da alocira

prostor.

Funkcija vraca pokazivac na novokreirani cvor */

Cvor *napravi_cvor(int broj)

{

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

if(novi == NULL) return NULL;

novi->vrednost = broj;

novi->sledeci = NULL;

return novi;

}

16

/* Funkcija oslobadja dinamicku memoriju zauzetu

od strane liste. */

void oslobodi_listu(Cvor ** glava)

{

Cvor *pomocni;

while (*glava != NULL) {

/* moramo najpre zapamtiti adresu sledeceg

elementa, a tek onda osloboditi glavu */

pomocni = (*glava)->sledeci;

free(*glava);

*glava = pomocni;

}

} 17

/* Funkcija dodaje novi cvor na pocetak liste. Funkcija kreira

novi cvor koriscenjem funkcije napravi_cvor(). */

void dodaj_na_pocetak_liste(Cvor ** glava, int broj)

{

Cvor *novi = napravi_cvor(broj);

/* provera greske prilikom alokacije */

if(novi == NULL){

fprintf(stderr,"malloc() greska u funkciji napravi_cvor()!\n");

oslobodi_listu(glava);

exit(EXIT_FAILURE);

}

/* uvezujemo novi cvor na pocetak */

novi->sledeci = *glava;

*glava = novi;

}

18

/* Funkcija dodaje novi cvor na kraj liste. Funkcija

kreira novi cvor koriscenjem funkcije napravi_cvor(). */

void dodaj_na_kraj_liste(Cvor ** glava, int broj)

{

Cvor *novi = napravi_cvor(broj);

Cvor *tekuci = *glava;

/* provera greske prilikom alokacije memorije */

if(novi == NULL){

fprintf(stderr,"malloc() greska u funkciji

napravi_cvor()!\n");

oslobodi_listu(glava);

exit(EXIT_FAILURE);

}

19

/* slucaj prazne liste. U tom slucaju je glava nove liste

upravo novi cvor. */

if (*glava == NULL) {

*glava = novi;

return;

}

/* Ako lista nije prazna, tada se krecemo duz liste sve

dok ne dodjemo do poslednjeg cvora (tj. do cvora ciji

pokazivac na sledeci pokazuje na NULL) */

while (tekuci->sledeci != NULL)

tekuci = tekuci->sledeci;

/* Dodajemo novi element na kraj preusmeravanjem

pokazivaca */

tekuci->sledeci = novi;

}

20

/* Funkcija dodaje novi element u sortiranu listu tako da i

nova lista ostane sortirana. Funkcija kreira novi cvor

koriscenjem funkcije napravi_cvor(). */

void dodaj_sortirano(Cvor ** glava, int broj)

{

Cvor *novi = napravi_cvor(broj);

Cvor *tekuci = *glava;

/* Proveravamo da li je doslo do greske prilikom alokacije

memorije */

if(novi == NULL){

fprintf(stderr,"malloc() greska u funkciji napravi_cvor()!\n");

oslobodi_listu(glava);

exit(EXIT_FAILURE);

}

21

/* u slucaju prazne liste glava nove liste je upravo

novi element */

if (*glava == NULL) {

*glava = novi;

return;

}

/* ako je novi element manji ili jednak od glave,

tada novi element mora da bude nova glava */

if ((*glava)->vrednost >= novi->vrednost) {

novi->sledeci = *glava;

*glava = novi;

return;

} 22

/* u slucaju da je glava manja od novog elementa, tada

se krecemo kroz listu sve dok se ne dodje do elementa

ciji je sledeci element veci ili jednak od novog elementa,

ili dok se ne dodje do poslednjeg elementa. */

while (tekuci->sledeci != NULL

&& tekuci->sledeci->vrednost < novi->vrednost)

tekuci = tekuci->sledeci;

/* U svakom slucaju novi element dodajemo IZA tekuceg

elementa */

novi->sledeci = tekuci->sledeci;

tekuci->sledeci = novi;

} 23

/* Funkcija trazi u listi element cija je vrednost

jednaka datom broju.

Funkcija vraca pokazivac na cvor liste u kome je

sadrzan trazeni broj ili NULL u slucaju da takav

element ne postoji u listi */

Cvor *pretrazi_listu(Cvor * glava, int broj)

{

for (; glava != NULL; glava = glava->sledeci)

if (glava->vrednost == broj)

return glava;

return NULL;

} 24

/* Funkcija brise iz liste sve cvorove koji sadrze

dati broj.

Funkcija vraca pokazivac na glavu liste (koji moze

biti promenjen u slucaju da se obrise stara glava) */

void obrisi_element(Cvor ** glava, int broj)

{

Cvor *tekuci;

Cvor *pomocni;

25

/* Brisemo sa pocetka liste sve eventualne cvorove koji su

jednaki datom broju, i azuriramo pokazivac na glavu */

while (*glava != NULL && (*glava)->vrednost == broj) {

pomocni = (*glava)->sledeci;

free(*glava);

*glava = pomocni;

}

/* Ako je nakon toga lista ostala prazna prekidamo

funkciju */

if (*glava == NULL)

return;

26

/* Od ovog trenutka se u svakom koraku nalazimo na

tekucem cvoru koji je razlicit od trazenog broja (kao i svi

levo od njega).

Poredimo vrednost sledeceg cvora (ako postoji) sa

trazenim brojem i brisemo ga ako je jednak, a prelazimo

na sledeci cvor ako je razlicit.

Ovaj postupak ponavljamo dok ne dodjemo do poslednjeg

cvora. */

tekuci = *glava;

while (tekuci->sledeci != NULL)

if (tekuci->sledeci->vrednost == broj) {

pomocni = tekuci->sledeci;

tekuci->sledeci = tekuci->sledeci->sledeci;

free(pomocni);

} else

tekuci = tekuci->sledeci;

return;}

27

/* Funkcija prikazuje elemente liste pocev

od glave ka kraju liste */

void ispisi_listu(Cvor * glava)

{

putchar('[');

for (; glava != NULL; glava = glava->sledeci)

printf("%d ", glava->vrednost);

putchar(']');

putchar('\n');

} ...test program ...

28

DVOSTRUKO POVEZANE LISTE.

#include <stdio.h>

#include <stdlib.h>

/* struktura kojom je predstavljen svaki element

liste */

typedef struct cvor{

int vrednost;

struct cvor *sledeci;

struct cvor *prethodni;

} Cvor; 29

/* Funkcija koja pravi nov cvor, inicijalizuje polje

vrednost u strukturi na broj i vraca pokazivac na

nov cvor */

Cvor* napravi_cvor(int broj)

{

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

if(novi == NULL)

return NULL;

/* inicijalizacija polja u novom cvoru */

novi->vrednost = broj;

novi->sledeci = NULL;

novi->prethodni = NULL;

return novi;

}

30

/* Funkcija oslobadja prostor koji smo alocirali za

elemente liste. */

Cvor* oslobodi_listu(Cvor* glava)

{

Cvor* pomocni;

while( glava != NULL )

{ pomocni = glava->sledeci;

free(glava);

glava = pomocni;

}

return NULL;

} 31

/* Fukcija kreira nov cvor sa poljem vrednost

postavljenim na broj i stavlja ga na pocetak liste. Nov

cvor je nova glava liste i vracamo pokazivac na nov

cvor. */

Cvor* dodaj_na_pocetak_liste(Cvor* glava, int

broj)

{

Cvor* novi = napravi_cvor(broj);

/* provera da li je doslo do greske prilikom alokacije */

if(novi == NULL)

{ fprintf(stderr, "malloc() greska u funkciji

napravi_cvor(%d)!\n",broj);

glava = oslobodi_listu(glava);

exit(EXIT_FAILURE);

}

32

/* sledeci od novog cvora je glava stare liste */

novi->sledeci = glava;

/* ako stara lista nije bila prazna, onda njena glava

nije NULL i moramo postaviti da prethodni od

glave bude nov cvor. */

if(glava != NULL)

glava->prethodni=novi;

return novi;

} 33

/* Funkcija nov cvor dodaje na kraj liste. */

Cvor* dodaj_na_kraj_liste( Cvor* glava, int broj)

{

Cvor* novi = napravi_cvor(broj);

Cvor* tekuci =glava;

/* provera da li je doslo do greske prilikom alokacije */

if(novi == NULL)

{ fprintf(stderr, "malloc() greska u funkciji

napravi_cvor(%d)!\n",broj);

glava = oslobodi_listu(glava);

exit(EXIT_FAILURE);

} 34

/* ako je lista u koju dodajemo prazna. Nov cvor je

jedini cvor u novoj listi i time je i glava nove liste.

Pa vracamo novi. */

if(glava == NULL)

return novi;

/* tekuci pokazivac pomeramo da na sledeci

element liste dok ne dodjemo do poslednjeg

elementa u listi (tj. elementa koji ima NULL

pokazivac na polju sledeci). */

while( tekuci->sledeci != NULL)

tekuci = tekuci->sledeci;

35

/* tada uvezujemo nov cvor na kraj, tako sto mu

azuriramo pokazivac na prethodni da pokazuje na

tekuci. A sledeci od tekuceg treba da bude nov cvor. */

tekuci->sledeci = novi;

novi->prethodni = tekuci;

return glava;

}

36

/* Fukcija dodaje u listu nov cvor na odgovarajuce

mesto. Tj. Funkcija pronalazi odgovarajuci cvor u listi

iza kod treba uvezati nov cvor. */

Cvor* dodaj_sortirano(Cvor* glava, int broj)

{

Cvor* novi = napravi_cvor(broj);

Cvor* tekuci = glava;

/* provera da li je doslo do greske prilikom alokacije */

if(novi == NULL)

{ fprintf(stderr, "malloc() greska u funkciji

napravi_cvor(%d)!\n",broj);

glava = oslobodi_listu(glava);

exit(EXIT_FAILURE);

}

37

/* ako je lista u koju ubacujemo nov cvor prazna,

nov cvor upravo i jedini cvor u novoj listi,

pa je i glava nove liste.*/

if( glava == NULL)

return novi;

/* Lista nije prazna*/

/* Ukoliko je vrednost glave liste veca od nove

vrednosti onda nov cvor treba da uvezati pre glave,

tj. staviti na pocetak liste. */

if( glava->vrednost >= broj)

{

novi->sledeci = glava;

glava->prethodni = novi;

return novi;

}

38

/* Pomeramo pokazivac tekuci da pokazuje na svoj

sledeci sve dok su vrednosti njegovog sledeceg manji od

nove vrednosti ili dok ne dodjemo do poslednjeg

elementa u listi.*/

while(tekuci->sledeci != NULL && tekuci->sledeci-

>vrednost < novi->vrednost)

tekuci = tekuci->sledeci;

novi->sledeci = tekuci->sledeci;

novi->prethodni = tekuci;

39

/* ako smo iz prethodne petlje izasli zato sto je

tekuci pokazivac na poslednji element u listi */

if( tekuci->sledeci != NULL )

tekuci->sledeci->prethodni = novi;

tekuci->sledeci = novi;

/* vracamo pokazivac na glavu cele liste*/

return glava;

}

40

/* Fukcija prolazi kroz listu od glave do kraja liste

u potrazi za cvorom koji na polju vrednost ima

trazenu vrednost broj. */

Cvor* pretrazi_listu(Cvor* glava, int broj)

{

for( ; glava!=NULL; glava = glava->sledeci)

if( glava->vrednost == broj)

/* nasli smo cvor sa trazenom vrednoscu i vracamo

pokazivac na njega. */

return glava;

/* dosli smo do kraja liste i nismo nasli trazeni

element. pa vracamo NULL. */

return NULL;

}

41

/* Funckija brise u listi na koju pokazuje pokazivac

glava bas onaj cvor na koji pokazuje pokazivac

tekuci. Obratiti paznju da je kod dvostruke liste

ovo mnogo lakse uraditi jer cvor tekuci sadrzi

pokazivace na svog sledbenika i prethodnika u listi.

Pre nego sto fizicki obrisemo tekuci obavezno

moramo azurirati sve pokazivace sledbenika i

prethodnika. */

Cvor* obrisi_tekuci(Cvor* glava, Cvor*

tekuci)

{

/* ako je tekuci NULL pokazivac nema sta da se

brise. */

if(tekuci == NULL)

return glava; 42

/* Ako postoji prethodnik od tekuceg onda se

postavlja da njegov sledeci bude sledeci od tekuceg */

if( tekuci->prethodni != NULL)

tekuci->prethodni->sledeci = tekuci->sledeci;

/* Ako postoji sledbenik tekuceg (cvora koji bismo

obrisali) onda njegov prethodnik treba da bude

prethodnik od tekuceg */

if(tekuci->sledeci != NULL)

tekuci->sledeci->prethodni =

tekuci->prethodni;

43

/* ako je glava element koji se brise. Glava nove

liste postaje sledbenik od glave. */

if( tekuci == glava)

glava = tekuci->sledeci;

/* oslobadjamo dinamicki alociran prostor za cvor

tekuci*/

free(tekuci);

return glava;

} 44

/* Brisemo element u listi cije polje vrednost je

trazeni broj */

Cvor* obrisi_element(Cvor* glava, int broj)

{

Cvor* tekuci = glava;

Cvor* pomocni;

while((tekuci = pretrazi_listu(glava,broj)) != NULL)

{ pomocni = tekuci->sledeci;

glava = obrisi_tekuci(glava, tekuci);

tekuci = pomocni;

}

return glava;

} 45

/* Funkcija ispisuje vrednosti iz liste. */

void ispisi_listu(Cvor* glava)

{

putchar('[');

for( ;glava != NULL; glava = glava->sledeci)

printf("%d ",glava->vrednost);

putchar(']');

putchar('\n');

}

46

/* Funkcija prikazuje elemente liste pocev od kraja

ka glavi liste. Kod dvostruko povezane to je jako

jednostavno jer svaki cvor ima pokazivac na

prethodni element u listi. */

void ispisi_listu_u_nazad(Cvor* glava)

{

putchar('[');

if(glava == NULL )

{ printf("]\n");

return;

}

47

/* pomeramo pokazivac glava sve dok ne pokazuje

na poslednji element u listi*/

while(glava->sledeci != NULL)

glava = glava->sledeci;

/* ispisujemo element po element liste unazad.

Pristupamo elementu liste i nakon ispisa

pomeramo pokazivac glava da pokazuje na

prethodnika */

for( ;glava != NULL; glava = glava->prethodni)

printf("%d ", glava->vrednost);

printf("]\n");

} ... test program ... 48

STEKOVI

Stek (eng. stack) je struktura podataka nad

kojom su definisane sledeće operacije:

1. Dodavanje elementa –

kažemo da je element potisnut na vrh steka

(eng. push() operacija)

2. Uklanjanje elementa koji je poslednji dodat –

kažemo da je element skinut sa vrha steka

(eng. pop() operacija)

3. Očitavanje vrednosti elementa koji je poslednji

dodat

(eng. top() operacija) 49

Stek spada u LIFO strukture

(eng. Last In First Out).

Može se implementirati na više načina.

Najjednostavniji način je da se definiše kao niz.

Međutim, tada je ograničen maksimalan broj elemenata

na steku dimenzijom niza.

Zbog toga se obično koriste liste za implementaciju steka,

gde se push() operacija svodi na dodavanje na početak, a

pop() operacija se svodi na uklanjanje glave liste.

Obe operacije se izvode u konstantnom vremenu.

50

/* Program proverava da li su etikete u datom

HTML fajlu dobro uparene */

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <ctype.h>

#define MAX 100

#define OTVORENA 1

#define ZATVORENA 2

#define VAN_ETIKETE 0

#define PROCITANO_MANJE 1

#define U_ETIKETI 2 51

/* Struktura koja predstavlja cvor liste */

typedef struct cvor {

char etiketa[MAX]; /* Sadrzi ime etikete */

struct cvor *sledeci; /* pokazivac na sledeci cvor */

} Cvor;

52

/* Funkcija kreira novi cvor, upisuje u njega etiketu

i vraca njegovu adresu */

Cvor *napravi_cvor(char *etiketa)

{

Cvor *novi = NULL;

if ((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL) {

fprintf(stderr, "malloc() greska!\n");

exit(1);

}

53

if (strlen(etiketa)>=MAX) {

fprintf(stderr, "Etiketa koju pokusavamo staviti na

stek preduga.\n");

exit(1);

}

strcpy(novi->etiketa, etiketa);

novi->sledeci = NULL;

return novi;

} 54

/* Funkcija postavlja na vrh steka novu etiketu */

void potisni_na_stek(Cvor ** vrh, char *etiketa)

{

Cvor *novi = napravi_cvor(etiketa);

novi->sledeci = *vrh;

*vrh = novi;

}

55

/* Funkcija skida sa vrha steka etiketu. Ako je

drugi argument pokazivac razlicit od NULL,

tada u niz karaktera na koji on pokazuje upisuje

ime etikete koja je upravo skinuta sa steka dok u

suprotnom ne radi nista.

Funkcija vraca 0 ako je stek prazan (pa samim tim

nije bilo moguce skinuti vrednost sa steka)

ili 1 u suprotnom. */

int skini_sa_steka(Cvor ** vrh, char *etiketa)

{

Cvor *pomocni;

56

/* pokusavamo da skinemo vrednost sa vrha

praznog steka i imamo gresku. */

if (*vrh == NULL)

return 0;

/* Ako adresa na koju zelimo da smestamo etiketu

nije NULL kopiramo tamo etiketu sa vrha steka. */

if (etiketa != NULL)

strcpy(etiketa, (*vrh)->etiketa);

/* oslobadjamo element sa vrha steka */

pomocni = *vrh;

*vrh = (*vrh)->sledeci;

free(pomocni);

return 1;

}

57

/* Funkcija vraca pokazivac na string koji sadrzi

etiketu na vrhu steka. Ukoliko je stek prazan,

vraca NULL */

char *vrh_steka(Cvor * vrh)

{

if (vrh == NULL)

return NULL;

return vrh->etiketa;

}

58

/* Funkcija prikazuje stek pocev od vrha prema dnu */

void prikazi_stek(Cvor * vrh)

{

for ( ; vrh != NULL; vrh = vrh->sledeci)

printf("<%s>\n", vrh->etiketa);

}

59

/* Funkcija prazni stek */

void oslobodi_stek(Cvor ** vrh)

{

Cvor *pomocni;

while (*vrh != NULL) {

pomocni = *vrh;

*vrh = (*vrh)->sledeci;

free(pomocni);

}

} 60

/* Funkcija iz fajla na koji pokazuje f cita sledecu

etiketu, i njeno ime upisuje u niz na koji pokazuje

pokazivac etiketa.

Funkcija vraca EOF u slucaju da se dodje do kraja fajla

pre nego sto se procita etiketa,

vraca OTVORENA ako je procitana otvorena etiketa,

odnosno ZATVORENA ako je procitana zatvorena etiketa.

*/

int uzmi_etiketu(FILE * f, char *etiketa)

{

61

int c; /* prihvata karakter iz datoteke*/

int stanje = VAN_ETIKETE; /* stanje nam cuva

informaciju dokle smo citali sa citanjem etikete

inicijalno smo ga postavili na vrednost

VAN_ETIKETE jer jos uvek nismo poceli da

citamo.*/

int i = 0;

int tip;

/* cuva informaciju o tipu etikete uzima vrednosti

OTVORENA, ZATVORENA */

62

while ((c = fgetc(f)) != EOF) {

switch (stanje) {

case VAN_ETIKETE:

if (c == '<')

stanje = PROCITANO_MANJE;

break;

63

case PROCITANO_MANJE:

if (c == '/')

/* citamo zatvarac */

tip = ZATVORENA;

else if (isalpha(c)) {

/* citamo otvarac */

tip = OTVORENA;

etiketa[i++] = tolower(c);

/* HTML je CaseInsensitive. U HTML-u etikete BODY i

body imaju isto znacenje, dok to u C-u nece vaziti.*/

}

stanje = U_ETIKETI;

/* poceli smo sa citanjem etikete, pa menjamo i stanje */

break; 64

case U_ETIKETI:

/* citamo etiketu */

if (isalpha(c) && i < MAX - 1)

/* ako je procitani karakter slovo i nismo premasili maksimalnu

dozvoljenu duzinu za etiketu*/

etiketa[i++] = tolower(c);

/* smestamo procitani karakter u etiketu*/

else {

stanje = VAN_ETIKETE;

/* u suprotnom, prestajemo sa citanjem etikete i menjamo stanje. */

etiketa[i] = '\0';

return tip;

/* zavrsili smo sa citanjem etikete i vracamo tip etikete koju smo

procitali, a ona nam je sacuvana nisci etiketa*/

}

break;}

65

}

/* dosli smo do kraja datoteke pre nego sto smo

zavrsili sa citanjem etikete , stoga imamo gresku i

vracamo EOF*/

return EOF;

}

66

/* Test program */

int main(int argc, char **argv)

{

Cvor *vrh = NULL;

char etiketa[MAX];

int tip;

int uparene = 1; /* na pocetku su nam etikete

upare, jer nismo nijednu jos uvek procitali. */

FILE *f; 67

/* Ime datoteke zadajemo na komandnoj liniji */

if (argc < 2) {

fprintf(stderr, "Koriscenje: %s ime_html_datoteke\n",

argv[0]);

exit(0);

}

/* Otvaramo datoteku */

if ((f = fopen(argv[1], "r")) == NULL) {

fprintf(stderr, "fopen() greska!\n");

exit(1);

} 68

/* Dokle god ima etiketa, uzimamo ih jednu po jednu

sa ulaza */

while ((tip = uzmi_etiketu(f, etiketa)) != EOF) {

/* Ako je otvorena etiketa, dodajemo je na stek.

Izuzetak su etikete <br>, <hr> i <meta> koje nemaju

sadrzaj, tako da ih nije potrebno zatvoriti.

NAPOMENA: U html-u postoje jos neke etikete koje

koje nemaju sadzaj (npr link). Pretpostavimo da njih

nema u dokumentu, zbog jednostavnosti */

if (tip == OTVORENA) {

if (strcmp(etiketa, "br") != 0 &&

strcmp(etiketa, "hr") != 0 &&

strcmp(etiketa, "meta") != 0)

potisni_na_stek(&vrh, etiketa);

} 69

/* Ako je zatvorena etiketa, tada je uslov dobre

uparenosti da je u pitanju zatvaranje etikete koja

je poslednja otvorena, a jos uvek nije zatvorena.

Ova etiketa se mora nalaziti na vrhu steka.

Ako je taj uslov ispunjen, tada je skidamo sa steka,

jer je zatvorena.

U suprotnom, obavestavamo korisnika da etikete

nisu pravilno uparene. */

70

else if (tip == ZATVORENA) {

if (vrh_steka(vrh) != NULL &&

strcmp(vrh_steka(vrh), etiketa) == 0)

skini_sa_steka(&vrh, NULL);

else {

printf(vrh_steka(vrh) != NULL ?

"Etikete nisu pravilno uparene\n(nadjena etiketa </%s> a

poslednja otvorena etiketa je <%s>)\n"

:

"Etikete nisu pravilno uparene\n(nadjena etiketa </%s>

koja nije otvorena)\n", etiketa, vrh_steka(vrh));

uparene = 0;

break; }

}

}

71

/* Zatvaramo fajl */

fclose(f);

/* Ako nismo pronasli pogresno uparivanje... */

if (uparene) {

/* Nakon zavrsetka citanja etiketa uslov je da stek

mora biti prazan. Ako nije, tada znaci da postoje jos

neke etikete koje su otvorene ali nisu zatvorene. */

72

if (vrh_steka(vrh) == NULL)

printf("Etikete su pravilno uparene!\n");

else

printf("Etikete nisu pravilno

uparene\n(etiketa <%s> nije zatvorena)\n",

vrh_steka(vrh));

}

/* Oslobadjamo stek */

oslobodi_stek(&vrh);

return 0;

} 73