You are on page 1of 25

Liste

Jednostruko povezana lista je struktura podataka koja se sastoji od sekvence čvorova. Svaki čvor
sadrži podatak (određenog tipa) i pokazivač na sledeći čvor u sekvenci. Prvi čvor u sekvenci
naziva se glava liste. Ostatak liste (bez glave) je takođe lista, i naziva se rep liste. Lista koja ne
sadrži čvorove naziva se prazna lista. Prilikom baratanja listom mi čuvamo pokazivač na glavu
liste. Kada pristupimo glavi liste, u njoj imamo zapisanu adresu sledećeg elementa, pa mu samim
tim mozemo pristupiti. Kada mu pristupimo, u njemu je sadržana adresa sledećeg elementa, pa
preko tog pokazivača možemo da mu pristupimo, itd. Poslednji element u listi nema sledeći
element: u tom slučaju se njegov pokazivač na sledeći postavlja na NULL. Takođe, prazna lista
se predstavlja NULL pokazivačem.

Prednost korišćenja povezanih lista u odnosu na dinamički niz je u tome što se elementi mogu
umetati i brisati sa bilo koje pozicije u nizu, bez potrebe za realokacijom ili premeštanjem
elemenata. Nedostatak ovakvog pristupa je to što ne možemo nasumično pristupiti proizvoljnom
elementu, već se elementi moraju obrađivati redom (iteracijom kroz listu).

Implementacija

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. 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. Ovo se može uraditi 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 nacin:
pok = funkcija_za_promenu(pok, ...);
tj. promenljivoj čija se vrednost predaje kao adresa glave u pozivu mora se dodeliti povratna
vrednost funkcije, za slu_caj da je adresa glave interno promenjena.
2. Pozvana funkcija koja vrši promenu na listi prihvata kao argument pokazivač na pokazivaku
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. Na primer:
funkcija_za_promenu(&pok, ...);
Funkcija koja se poziva je po pravilu tipa void. Prednost drugog metoda je jednostavnije
pozivanje, dok je prednost prvog metoda jednostavnija sintaksa unutar pozvane funkcije. U
prethodnom primeru je demonstriran prvi pristup. Naredni primer ilustruje drugi pristup.

Primer1: Osnovne funkcije za rad sa listama.


#include <stdio.h>
#include <stdlib.h>

/* Struktura koja predstavlja cvor liste */


typedef struct cvor {
int vrednost; /* podatak koji cvor sadrzi */
struct cvor *sledeci; /* pokazivac na sledeci cvor */
} Cvor;

/* 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 = NULL;
if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)
{
fprintf(stderr,"malloc() greska!\n");
exit(1);
}
novi->vrednost = broj;
novi->sledeci = NULL;
return novi;
}

/* Funkcija dodaje novi cvor na pocetak liste. Funkcija kreira novi cvor koriscenjem funkcije
napravi_cvor(). Funkcija vraca pokazivac na novu glavu liste */
Cvor* dodaj_na_pocetak_liste(Cvor *glava, int broj)
{
Cvor * novi = napravi_cvor(broj);
novi->sledeci = glava;
return novi;
}

/*void dodaj_na_pocetak_liste(Cvor **glava, int broj)


{
Cvor *novi = napravi_cvor(broj);
novi->sledeci = *glava;
*glava = novi;
}
*/

/* Funkcija dodaje novi cvor na kraj liste. Funkcija kreira novi cvor koriscenjem funkcije
napravi_cvor(). Funkcija vraca pokazivac na glavu liste (koji moze
biti promenjen u slucaju da je lista inicijalno bila prazna). */
Cvor* dodaj_na_kraj_liste(Cvor *glava, int broj)
{
Cvor * novi = napravi_cvor(broj);
Cvor *tekuci = glava;
/* slucaj prazne liste. U tom slucaju je glava nove liste upravo novi cvor. */
if(glava == NULL) return novi;
/* 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;
/* Vracamo pokazivac na glavu liste */
return glava;
}

/*void dodaj_na_kraj_liste(Cvor **glava, int broj)


{
Cvor *novi = napravi_cvor(broj);
Cvor *tekuci = *glava;
/* 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;
}
*/

/* 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;
}
/* Funkcija prikazuje elemente liste pocev od glave ka kraju liste */
void prikazi_listu(Cvor *glava)
{
putchar('[');
for(;glava != NULL; glava = glava->sledeci)
printf("%d ", glava->vrednost);
putchar(']');
putchar('\n');
}

/* Funkcija oslobadja dinamicku memoriju zauzetu od strane liste. Funkcija vraca NULL, tj.
vrednost koju treba dodeliti pokazivackoj promenljivoj, s obzirom da je sada lista prazna. */
Cvor* 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;
}
return NULL;
}

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

/* Funkcija dodaje novi element u sortiranu listu tako da i nova lista ostane sortirana. Funkcija
kreira novi cvor koriscenjem funkcije napravi_cvor(). Funkcija vraca pokazivac na glavu liste
(koji moze biti promenjen u slucaju da je novi element dodat na pocetak liste) */
Cvor* dodaj_sortirano(Cvor *glava, int broj)
{
Cvor *novi = napravi_cvor(broj);
Cvor *tekuci = glava;
/* u slucaju prazne liste glava nove liste je upravo novi element */
if(glava == NULL) return novi;
/* 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;
return novi;
}
/* 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;
/* vracamo pokazivac na glavu liste*/
return glava;
}

/* void dodaj_sortirano(Cvor **glava, int broj)


{
Cvor *novi = napravi_cvor(broj);
Cvor *tekuci = *glava;
/* 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;
}
/* 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;
}
*/

/* 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) */
Cvor* obrisi_element(Cvor *glava, int broj)
{
Cvor *tekuci;
Cvor *pomocni;
/* 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 vracamo NULL */
if(glava == NULL) return NULL;
/* 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;
/* vracamo novu glavu */
return glava;
}

/* void obrisi_element(Cvor **glava, int broj)


{
Cvor *tekuci;
Cvor *pomocni;
/* 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;
/* 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;
}
*/

/* test program */
int main()
{
Cvor *glava = NULL;
Cvor *pomocni = NULL;
int broj;
/* Testiranje dodavanja na pocetak */
printf("-------------------------------------------------------\n");
printf("---------- Testiranje dodavanja na pocetak ------------\n");
do {
printf("-------------------------------------------------------\n");
printf("Prikaz trenutnog sadrzaja liste:\n");
prikazi_listu(glava);
printf("-------------------------------------------------------\n");
printf("Dodati element na pocetak liste (ctrl-D za kraj unosa):\n");
} while(scanf("%d", &broj) > 0 &&
(glava = dodaj_na_pocetak_liste(glava, broj)) );
/* dodaj_na_pocetak_liste(&glava, broj);*/
printf("-------------------------------------------------------\n");
putchar('\n');
/* Testiranje pretrage elementa */
printf("-------------------------------------------------------\n");
printf("---------------- Testiranje pretrage ------------------\n");
printf("-------------------------------------------------------\n");
printf("Prikaz trenutnog sadrzaja liste:\n");
prikazi_listu(glava);
printf("-------------------------------------------------------\n");
printf("Uneti broj koji se trazi: ");
scanf("%d",&broj);
if((pomocni = pretrazi_listu(glava, broj)) == NULL)
printf("Trazeni broj nije u listi\n");
else
printf("Trazeni element %d je u listi\n", pomocni->vrednost);
printf("-------------------------------------------------------\n");
putchar('\n');
glava = oslobodi_listu(glava);
/*oslobodi_listu(&glava);*/
/* Testiranje dodavanja na kraj */
printf("-------------------------------------------------------\n");
printf("------------ Testiranje dodavanja na kraj -------------\n");
do{
printf("-------------------------------------------------------\n");
printf("Prikaz liste:\n");
prikazi_listu(glava);
printf("-------------------------------------------------------\n");
printf("Dodati element na kraj liste (ctrl-D za kraj unosa):\n");
} while(scanf("%d",&broj) > 0 &&
(glava = dodaj_na_kraj_liste(glava, broj)));
/*(dodaj_na_kraj_liste(&glava,broj)));*/
printf("-------------------------------------------------------\n");
putchar('\n');
/* Testiranje brisanja elemenata */
printf("-------------------------------------------------------\n");
printf("--------------- Testiranje brisanja -------------------\n");
printf("-------------------------------------------------------\n");
printf("Prikaz trenutnog sadrzaja liste:\n");
prikazi_listu(glava);
printf("-------------------------------------------------------\n");
printf("Uneti broj koji se brise: ");
scanf("%d", &broj);
glava = obrisi_element(glava, broj);
/* obrisi_element(&glava, broj); */
printf("-------------------------------------------------------\n");
printf("Lista nakon izbacivanja:\n");
prikazi_listu(glava);
printf("-------------------------------------------------------\n");
putchar('\n');
glava = oslobodi_listu(glava);
/*oslobodi_listu(&glava);*/
/* Testiranje sortiranog dodavanja */
printf("-------------------------------------------------------\n");
printf("----------- Testiranje sortiranog dodavanja -----------\n");
do{
printf("-------------------------------------------------------\n");
printf("Prikaz liste:\n");
prikazi_listu(glava);
printf("-------------------------------------------------------\n");
printf("Dodati element sortirano (ctrl-D za kraj unosa):\n");
} while(scanf("%d",&broj) > 0 &&
(glava = dodaj_sortirano(glava, broj)));
/*(dodaj_sortirano(&glava, broj)));*/
printf("-------------------------------------------------------\n");
putchar('\n');
glava = oslobodi_listu(glava);
/*oslobodi_listu(&glava);*/
return 0;
}

Primer2: Program ispisuje broj pojavljivanja za svaku od reči koja se pojavila u tekstu unetom sa
standardnog ulaza. Verzija sa (sortiranom) listom.

#include <stdlib.h>
#include <stdio.h>

/* Definicija cvora liste */


typedef struct _cvor
{
char ime[80];
int br_pojavljivanja;
struct _cvor* sledeci;
} cvor;

/* Pomocna funkcija koja kreira cvor. Funkcija vraca pokazivac na novokreirani cvor */
cvor* napravi_cvor(char* rec)
{
cvor *novi = NULL;
if((novi = (cvor *) malloc(sizeof(cvor))) == NULL)
{
fprintf(stderr,"malloc() greska!\n");
exit(1);
}
strcpy(novi->ime, rec);
novi->br_pojavljivanja = 1;
novi->sledeci = NULL;
return novi;
}

/* Funkcija ispisuje listu rekurzivno, pocevsi od poslednjeg elementa */


void ispisi_listu(cvor* pocetak)
{
if (pocetak!=NULL)
{
ispisi_listu(pocetak->sledeci);
printf("%s %d\n",pocetak->ime,pocetak->br_pojavljivanja);
}
}

/* Funkcija koja brise listu */


void obrisi_listu(cvor* pocetak)
{
if (pocetak!=NULL)
{
obrisi_listu(pocetak->sledeci);
free(pocetak);
}
}

/* Funkcija ubacuje rekurzivno datu rec u listu koja je leksikografski sortirana, na odgovarajuce
mesto i vraca pokazivac na novi pocetak liste */
cvor* ubaci_sortirano(cvor* pocetak, char* rec)
{
int cmp;
/* Ukoliko je lista prazna ubacujemo na pocetak liste*/
if (pocetak==NULL)
return napravi_cvor(rec);
/* Ukoliko lista nije prazna poredimo rec sa elementom u glavi */
cmp=strcmp(pocetak->ime,rec);
/* Ukoliko je rec pronadjena samo uvecavamo njen broj pojavljivanja */
if (cmp==0)
{
pocetak->br_pojavljivanja++;
return pocetak;
}
/* Ukoliko je rec koju ubacujemo veca od tekuce reci, ubacujemo je rekurzivno u rep */
else if (cmp>0)
{
pocetak->sledeci=ubaci_sortirano(pocetak->sledeci,rec);
return pocetak;
}
/* Ukoliko je rec koju ubacujemo manja od tekuce reci, gradimo novi cvor I ubacujemo
ga ispred pocetka */
else
{
cvor* novi=napravi_cvor(rec);
novi->sledeci=pocetak;
return novi;
}
}

/* Pomocna funkcija koja cita rec sa standardnog ulaza i vraca njenu duzinu, odnosno -1 ukoliko
se naidje na EOF */
int getword(char word[], int lim) {
int c, i=0;
while (!isalpha(c=getchar()) && c!=EOF)
;
if (c==EOF)
return -1;
do
{
word[i++]=c;
}while (i<lim-1 && isalpha(c=getchar()));
word[i]='\0';
return i;
}

/* Funkcija koja rekurzivno pronalazi datu rec u datoj listi. Funkcija vraca pokazivac na cvor u
kome je nadjena rec, ili NULL ukoliko rec nije nadjena. */
cvor* nadji_rec(cvor* lista, char rec[])
{
if (lista==NULL)
return NULL;
if (strcmp(lista->ime,rec)==0)
return lista;
/* Da bi pretrazivanje u sortiranoj listi bilo efikasnije:
if (strcmp(lista->ime,rec)<0)
return NULL;
*/
return nadji_rec(lista->sledeci,rec);
}

main()
{
cvor* lista=NULL;
char procitana_rec[80];
cvor* pronadjen;
while(getword(procitana_rec,80)!=-1)
lista=ubaci_sortirano(lista,procitana_rec);
pronadjen=nadji_rec(lista,"programiranje");
if (pronadjen!=NULL)
printf("Rec programiranje se javlja u listi!\n");
else
printf("Rec programiranje se ne javlja u listi!\n");
ispisi_listu(lista);
obrisi_listu(lista);
}

Primer3: Program formira celobrojni red i ispisuje sadržaj reda na standardni izlaz.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100

/* Struktura koja predstavlja cvor liste */


typedef struct cvor {
int broj; /* broj */
struct cvor *sledeci; /* pokazivac na sledeci cvor */
} Cvor;

/* Funkcija kreira novi cvor, upisuje u njega broj i vraca njegovu adresu */
Cvor * napravi_cvor(int broj)
{
Cvor *novi = NULL;
if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)
{
fprintf(stderr,"malloc() greska!\n");
exit(1);
}
novi->broj = broj;
novi->sledeci = NULL;
return novi;
}

/* Funkcija dodaje na kraj reda broj */


void dodaj_u_red(Cvor **pocetak, Cvor **kraj, int broj)
{
Cvor * novi = napravi_cvor(broj);
if(*kraj != NULL)
{
(*kraj)->sledeci = novi;
*kraj = novi;
}
else /* ako je red prazan */
{
*pocetak = novi;
*kraj = novi;
}
}

/* Funkcija skida sa pocetka reda broj */


int skini_sa_reda(Cvor **pocetak, Cvor ** kraj, int *broj)
{
Cvor * pomocni;
if(*pocetak == NULL)
return 0;
if(broj != NULL)
*broj = (*pocetak)->broj);
pomocni = *pocetak;
*pocetak = (*pocetak)->sledeci;
free(pomocni);
if(*pocetak == NULL)
*kraj = NULL;
return 1;
}

main()
{
Cvor * pocetak = NULL, * kraj = NULL;
int i, broj;
/*Formiranje jednostavnog reda*/
for(i=0; i<100; i++)
dodaj_u_red(&pocetak, &kraj, i);
/*Ispis elemenata reda koristeci operacije nad redom*/
while(skini_sa_reda(&pocetak, &kraj, &broj))
printf("Element uzet sa pocetka reda je %d\n", broj);
/*Nema potrebe da se oslobadja memorija jer je memorija koju
je red zauzimao oslobodjena prilikom ispisa elemenata
reda - nakon ispisa red je ostao prazan!*/
}

Stek

Stek (eng. stack) je struktura podataka nad kojom su definisane sledece operacije:
 Dodavanje elementa { kazemo da je element potisnut na vrh steka (eng. push()
operacija).
 Uklanjanje elementa koji je poslednji dodat { kazemo da je element skinut sa vrha steka
(eng. pop() operacija).
 Ocitavanje vrednosti elementa koji je poslednji dodat (eng. top() operacija).
Stek spada u LIFO strukture (eng. Last In First Out). Moze se implementirati na vise nacina.
Najjednostavniji nacin je da se definise kao niz. Medjutim, tada je ogranicen max. broj
elemenata na steku dimenzijom niza. Zbog toga se obično pribegava koriscenju lista za
implementaciju steka, gde se push() operacija svodi na dodavanje na pocetak, a pop() operacija
se svodi na uklanjanje glave liste. Obe operacije se izvode u konstantnom vremenu.

Primer4: Program formira celobrojni stek i ispisuje sadržaj steks na standardni izlaz.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100

/* Struktura koja predstavlja cvor liste */


typedef struct cvor {
int broj; /* broj */
struct cvor *sledeci; /* pokazivac na sledeci cvor */
} Cvor;

/* Funkcija kreira novi cvor, upisuje u njega broj


i vraca njegovu adresu */
Cvor * napravi_cvor(int broj)
{
Cvor *novi = NULL;
if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)
{
fprintf(stderr,"malloc() greska!\n");
exit(1);
}
novi->broj = broj;
novi->sledeci = NULL;
return novi;
}

/* Funkcija dodaje broj na stek*/


void potisni_na_stek(Cvor **vrh, int broj)
{
Cvor * novi = napravi_cvor(broj);
novi->sledeci = *vrh;
*vrh = novi;
}

/* Funkcija skida broj sa steka */


int skini_sa_steka(Cvor **vrh, int *broj)
{
Cvor * pomocni;
if(*vrh == NULL)
return 0;
if(broj != NULL)
*broj = (*vrh)->broj);
pomocni = *vrh;
*vrh = (*vrh)->sledeci;
free(pomocni);
return 1;
}

main()
{
Cvor * vrh = NULL;
int i, broj;
/*Formiranje steka*/
for(i=0; i<100; i++)
potisni_na_stek(&vrh, i);
/*Ispis elemenata steka: 99, 98 ... 0*/
while(skini_sa_steka(&pocetak, &kraj, &broj))
printf("Broj skinut sa vrha steka je %d\n", broj);
/*Nema potrebe da se oslobadja memorija jer je memorija koju
je stek zauzimao oslobodjena prilikom ispisa elemenata
steka - nakon ispisa stek je ostao prazan!*/
}

Dvostruko povezane liste


Dvostruko povezana lista je struktura podataka koja se sastoji od sekvence čvorova. Svaki čvor
sadrži podatak (određenog tipa) i pokazivače na prethodni i sledeći čvor u sekvenci. Za razliku
od obične liste, u svakom čvoru imamo zapisanu i adresu prethodnog elementa, pa se kroz listu
možemo kretati i unazad. Poslednji element u listi nema sledeći element: u tom slučaju se njegov
pokazivač na sledeći postavlja na NULL. Slično, glava liste nema prethodni element, pa je njen
pokazivač na prethodni postavljen na NULL. Takođe, prazna lista se predstavlja NULL
pokazivačem.

Prednost dvostruko povezane liste u odnosu na jednostruko povezanu je u tome što se možemo
kretati u oba smera kroz listu. Nedostatak je to što se veličina memorije potrebne za čuvanje
čvora povećava za veličinu jednog pokazivača.

Primer5: Program demonstrira rad sa dvostruko povezanim listama.


#include <stdio.h>
#include <stdlib.h>

/* Struktura koja predstavlja cvor liste */


typedef struct cvor {
int vrednost; /* podatak koji cvor sadrzi */
struct cvor *sledeci; /* pokazivac na sledeci cvor */
struct cvor *prethodni; /* pokazivac na prethodni */
} Cvor;

/* Pomocna funkcija koja kreira cvor. Funkcija vrednost novog cvora inicijalizuje na broj, dok
pokazivace na prethodni i 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 = NULL;
if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)
{
fprintf(stderr,"malloc() greska!\n");
exit(1);
}
novi->vrednost = broj;
novi->prethodni = NULL;
novi->sledeci = NULL;
return novi;
}

/* Funkcija dodaje novi cvor na pocetak liste. Funkcija kreira novi cvor koriscenjem funkcije
napravi_cvor(). Funkcija vraca pokazivac na novu glavu liste */
Cvor* dodaj_na_pocetak_liste(Cvor *glava, int broj)
{
Cvor *novi = napravi_cvor(broj);
novi->sledeci = glava;
if(glava != NULL) glava->prethodni = novi;
return novi;
}

/* Funkcija dodaje novi cvor na kraj liste. Funkcija kreira novi cvor koriscenjem funkcije
napravi_cvor().vFunkcija vraca pokazivac na glavu liste (koji moze biti promenjen u slucaju da
je lista inicijalno bila prazna). */
Cvor* dodaj_na_kraj_liste(Cvor *glava, int broj)
{
Cvor *novi = napravi_cvor(broj);
Cvor *tekuci = glava;
/* slucaj prazne liste. U tom slucaju je glava nove liste upravo novi cvor. */
if(glava == NULL) return novi;
/* 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;
novi->prethodni = tekuci;
/* Vracamo glavu liste */
return glava;
}

/* Funkcija dodaje novi element u sortiranu listu tako da i nova lista ostane sortirana. Funkcija
kreira novi cvor koriscenjem funkcije napravi_cvor(). Funkcija vraca pokazivac na glavu liste
(koji moze biti promenjen u slucaju da je novi element dodat na pocetak liste) */
Cvor* dodaj_sortirano(Cvor *glava, int broj)
{
Cvor *novi = napravi_cvor(broj);
Cvor *tekuci = glava;
/* u slucaju prazne liste glava nove liste je upravo novi element */
if(glava == NULL) return novi;
/* 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->prethodni = novi;
return novi;
}
/* 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;
novi->prethodni = tekuci;
if(tekuci->sledeci != NULL)
tekuci->sledeci->prethodni = novi;
tekuci->sledeci = novi;
/* vracamo vrednost glave */
return glava;
}

/* 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)
{
/* Pretrazivanje se vrsi bez pretpostavke o sortiranosti liste.*/
for(; glava != NULL ; glava = glava->sledeci)
if(glava->vrednost == broj)
return glava;
return NULL;
}

/* Funkcija brise tekuci element liste, tj. element liste na koji pokazuje pokazivac tekuci.
Funkcija vraca pokazivac na glavu liste, koja moze biti promenjena ako je upravo
glava obrisani cvor */
Cvor* obrisi_tekuci(Cvor * glava, Cvor * tekuci)
{
if(tekuci == NULL)
return glava;
/* Preusmeravamo pokazivace prethodnog i sledeceg ako oni postoje */
if(tekuci->prethodni != NULL)
tekuci->prethodni->sledeci = tekuci->sledeci;
if(tekuci->sledeci != NULL)
tekuci->sledeci->prethodni = tekuci->prethodni;
/* Ako je cvor koji brisemo glava, tada moramo da promenimo glavu (sledeci element
postaje glava) */
if(tekuci == glava)
glava = tekuci->sledeci;
/* Brisemo tekuci cvor */
free(tekuci);
return glava;
}

/* 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) */
Cvor* obrisi_element(Cvor *glava, int broj)
{
Cvor *tekuci = glava;
Cvor *pomocni;
/* Pretrazujemo listu pocev od tekuceg elementa trazeci datu vrednost. Ako je
pronadjemo, brisemo nadjeni cvor i nastavljamo pretragu od elementa koji sledi nakon
upravo obrisanog */
while((tekuci = pretrazi_listu(tekuci, broj)) != NULL)
{
pomocni = tekuci->sledeci;
glava = obrisi_tekuci(glava, tekuci);
tekuci = pomocni;
}
/* vracamo novu glavu */
return glava;
}

/* Funkcija prikazuje elemente liste pocev od glave ka kraju liste */


void prikazi_listu(Cvor *glava)
{
putchar('[');
for(;glava != NULL; glava = glava->sledeci)
printf("%d ", glava->vrednost);
putchar(']');
putchar('\n');
}

/* Funkcija prikazuje elemente liste u obrnutom poretku */


void prikazi_listu_obrnuto(Cvor * glava)
{
/* Ako je lista prazna... */
if(glava == NULL)
{
printf("[]\n");
return;
}
/* Prolazimo kroz listu dok ne stignemo do poslednjeg elementa. */
while(glava->sledeci != NULL)
glava = glava->sledeci;
/* Ispisujemo elemente liste iteracijom u suprotnom smeru */
putchar('[');
for(;glava != NULL; glava = glava->prethodni)
printf("%d ", glava->vrednost);
putchar(']');
putchar('\n');
}

/* Funkcija oslobadja dinamicku memoriju zauzetu od strane liste. Funkcija vraca NULL, tj.
vrednost koju treba dodeliti pokazivackoj promenljivoj, s obzirom da je sada lista prazna. */
Cvor* 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;
}
return NULL;
}

Stabla
Binarno stablo1 je skup čvorova koji su povezani na sledeći način:
1. Svaki čvor moze imati levog i desnog SINA (pri tom svaki od sinova moze biti i izostavljen,
ili oba). Kažemo da je dati čvor RODITELJ svojim sinovima. Ako je čvor U roditelj čvoru V,
tada pišemo da je U < V . Čvor koji nema ni levog ni desnog sina naziva se LIST.
2. Postoji jedan jedinstveni čvor takav da nema roditelja. Ovaj čvor nazivamo KOREN stabla.
3. U stablu nema ciklusa, tj. ne postoji niz čvorova x1,x2,...,xn, takav da je x1 < x2 < ::: < xn <
x1.

Inace, niz čvorova x1 < x2 < ::: < xn nazivamo put u stablu. Kažemo da je čvor U predak čvora
V ako u stablu postoji put od U do V. Specijalno, svaki čvor je predak samom sebi. Lako se
može pokazati da je koren predak svih čvorova u stablu. Rastojanje od korena do nekog čvora
naziva se visina čvora (koren je visine 0). Maksimalna visina čvora u stablu naziva se visina
stabla.

Stablo koje nema čvorova naziva se prazno stablo. Za svaki čvor V u stablu možemo posmatrati
stablo koje se sastoji od svih njegovih potomaka (svih čvorova kojima je on predak). Ovo stablo
se naziva podstablo sa datim korenom V. Podstablo sa njegovim levim sinom kao korenom
nazivamo levim podstablom čvora V, dok podstablo sa njegovim desnim sinom kao korenom
nazivamo desnim podstablom čvora V. Ako je neki od njegovih sinova izostavljen, tada kažemo
da je odgovarajuće podstablo čvora V prazno stablo. Specijalno, ako je V koren, tada njegovo
levo i desno podstablo nazivamo levim i desnim podstablom datog (čitavog) stabla.

Stablo se može definisati i rekurzivno na sledeći način:


1. Prazno stablo je stablo
2. Ako su data dva stabla t1 i t2, i čvor r, tada je i (t1,r,t2) takodje stablo. t1 je tada levo
podstablo, t2 je desno podstablo dok je čvor r koren tako formiranog stabla.

U programiranju se stabla obično koriste kao strukture podataka, tako što svaki čvor sadrži po
jedan podatak određenog tipa. Način raspoređivanja podataka u stablu zavisi od konkretne
primene stabla. S obzirom na rekurzivnu prirodu stabla, uobičajeno je da se u programiranju
stabla obrađuju rekurzivno.

Binarno pretraživačko stablo

Posebna vrsta stabla su tzv. binarna pretraživačka stabla. Ova stabla imaju osobinu da za svaki
čvor važi sledeće: svi čvorovi njegovog levog podstabla sadrže podatke sa manjom vrednošću od
vrednosti podatka u tom čvoru, dok svi čvorovi njegovog desnog podstabla sadrže podatke sa
većom vrednošću od vrednosti podatka u tom čvoru. Ovakva organizacija omogućava efikasno
pretraživanje.

Primer6: Program demonstrira rad sa binarnim pretrazivackim drvetima čiji su podaci celi
brojevi. Drvo sadrži cele brojeve sortirane po veličini. Za svaki čvor, levo podstablo sadrži
manje elemente, dok desno podstablo sadrži veće.

#include <stdlib.h>
#include <stdio.h>
/* Struktura koja predstavlja cvor drveta */
typedef struct _cvor
{
int broj;
struct _cvor *l, *d;
} cvor;
/
* Pomocna funkcija za kreiranje cvora. */
cvor* napravi_cvor(int b) {
cvor* novi = (cvor*)malloc(sizeof(cvor));
if (novi == NULL)
{
fprintf(stderr, "Greska prilikom
alokacije memorije");
exit(1);
}
novi->broj = b;
novi->l = NULL;
novi->d = NULL;
return novi;
}

/* Funkcija umece broj b u drvo ciji je koren dat preko pokazivaca koren. Funkcija vraca
pokazivac na koren novog drveta */
cvor* ubaci_u_drvo(cvor* koren, int b)
{
if (koren == NULL)
return napravi_cvor(b);
if (b < koren->broj)
koren->l = ubaci_u_drvo(koren->l, b);
else
koren->d = ubaci_u_drvo(koren->d, b);
return koren;
}

/* Funkcija proverava da li dati broj postoji u drvetu */


int pronadji(cvor* koren, int b)
{
if (koren == NULL)
return 0;
if (koren->broj == b)
return 1;
if (b < koren->broj)
return pronadji(koren->l, b);
else
return pronadji(koren->d, b);
}

/* Funkcija ispisuje sve cvorove drveta u infiksnom redosledu */


void ispisi_drvo(cvor* koren) {
if (koren != NULL)
{
ispisi_drvo(koren->l);
printf("%d ", koren->broj);
ispisi_drvo(koren->d);
}
}

/* Funkcija oslobadja memoriju koju je drvo zauzimalo */


void obrisi_drvo(cvor* koren) {
if (koren != NULL)
{
/* Oslobadja se memorija za levo poddrvo */
obrisi_drvo(koren->l);
/* Oslobadja se memorija za desno poddrvo */
obrisi_drvo(koren->d);
/* Oslobadja se memorija koju zauzima koren */
free(koren);
}
}

/* Funkcija sumira sve vrednosti binarnog stabla */


int suma_cvorova(cvor* koren)
{
if (koren == NULL)
return 0;
return suma_cvorova(koren->l) + koren->broj + suma_cvorova(koren->d);
}

/* Funkcija prebrojava broj cvorova binarnog stabla */


int broj_cvorova(cvor* koren)
{
if (koren == NULL)
return 0;
return broj_cvorova(koren->l) + 1 + broj_cvorova(koren->d);
}

/* Funkcija prebrojava broj listova binarnog stabla */


int broj_listova(cvor* koren)
{
if (koren == NULL)
return 0;
if (koren->l == NULL && koren->d == NULL)
return 1;
return broj_listova(koren->l) + broj_listova(koren->d);
}

/* Funkcija izracunava sumu listova binarnog stabla */


int suma_listova(cvor* koren)
{
if (koren == NULL)
return 0;
if (koren->l == NULL && koren->d == NULL)
return koren->broj;
return suma_listova(koren->l) +
suma_listova(koren->d);
}

/* Funkcija ispisuje sadrzaj listova binarnog stabla */


void ispisi_listove(cvor* koren)
{
if (koren == NULL)
return;
ispisi_listove(koren->l);
if (koren->l == NULL && koren->d == NULL)
printf("%d ", koren->broj);
ispisi_listove(koren->d);
}

/* Funkcija pronalazi maksimalnu vrednost u drvetu Koristi se cinjenica da je ova vrednost


smestena u najdesnjem listu */
int max_vrednost(cvor* koren)
{
if (koren==NULL)
return 0;
if (koren->d==NULL)
return koren->broj;
return max_vrednost(koren->d);
}

/* Iterativna funkcija za pronalazenje maksimalne vrednosti. */


int max_vrednost_nerekurzivno(cvor* koren)
{
if (koren==NULL)
return 0;
else
{
cvor* tekuci;
for (tekuci=koren; tekuci->d!=NULL; tekuci=tekuci->d)
;
return tekuci->broj;
}
}

#define max(a,b) (((a)>(b))?(a):(b))

/* Funkcija racuna "dubinu" binarnog stabla */


int dubina(cvor* koren)
{
if (koren==NULL)
return 0;
else
{
int dl=dubina(koren->l);
int dd=dubina(koren->d);
return 1+max(dl,dd);
}
}

/* Program koji testira rad prethodnih funkcija */


main()
{
cvor* koren = NULL;
koren = ubaci_u_drvo(koren, 1);
koren = ubaci_u_drvo(koren, 8);
koren = ubaci_u_drvo(koren, 5);
koren = ubaci_u_drvo(koren, 3);
koren = ubaci_u_drvo(koren, 7);
koren = ubaci_u_drvo(koren, 6);
koren = ubaci_u_drvo(koren, 9);
if (pronadji(koren, 3))
printf("Pronadjeno 3\n");
if (pronadji(koren, 2))
printf("Pronadjeno 2\n");
if (pronadji(koren, 7))
printf("Pronadjeno 7\n");
ispisi_drvo(koren);
putchar('\n');
printf("Suma cvorova : %d\n", suma_cvorova(koren));
printf("Broj cvorova : %d\n", broj_cvorova(koren));
printf("Broj listova : %d\n", broj_listova(koren));
printf("Suma listova : %d\n", suma_listova(koren));
printf("Dubina drveta : %d\n", dubina(koren));
printf("Maximalna vrednost : %d\n", max_vrednost(koren));
ispisi_listove(koren);
obrisi_drvo(koren);
}
/*
Pronadjeno 3
Pronadjeno 7
1356789
Suma cvorova : 39
Broj cvorova : 7
Broj listova : 3
Suma listova : 18
Dubina drveta : 5
Maximalna vrednost : 9
369
*/

You might also like