Professional Documents
Culture Documents
Povezane Liste
Povezane Liste
Povezane Liste
Za pohranjivanje skupa podataka istoga tipa do sada smo koristili nizove. Neka imamo slijedede
deklaracije:
struct ucenik
{ char ime[20]; // ime do 20 karaktera
int godine; // godine starosti
float visina; // u metrima
}
ucenik razred[30];
Prethodnim naredbama smo deklarisali strukturu ucenik i niz ciji su elementi tipa strukture
ucenik.
Prilikom deklaracije niza moramo navesti maksimalan broj elemenata niza, što predstavlja
veliko ograničenje ove vrste podataka. Naime, ukoliko navedemo veliki broj elemenata,
rizikujemo da de program nepotrebno zauzeti veliki memorijski prostor. Ako navedemo mali
broj elemenata, rizikujemo da program nede zadovoljavati potrebe korisnika. Stoga, potreba
dimenzionisanja niza gotovo uvijek predstavlja problem, jer je nemogude predvidjeti koliko
elemenata niz uistinu ima kada se program pokrene.
Kao rješenje ovog problema može se koristiti povezana lista. Međutim, svako rješenje ima i
odgovarajude mane. Jedna od mana povezane liste jeste i to što imaju loše performance kada je
u pitanju slučajan pristup.
Postoje jednostruko i dvostruko povezane liste. Jednostruko povezana lista sadrži pokazivač na slijededi
čvor, a dvostruko povezane sadrži dva pokazivača: jedan pokazuje na slijededi a jedan na prethodni čvor.
Najprije demo upoznati jednostruko povezanu listu.
START
Ime: Fred Ime: Ime Ime: Joe Ime: Zoe
Godine: 34 Godine: 27 Godine: 48 Godine: 30
Visina: 1.7 Visina: 1.2 Visina: 1.4 Visina: 1.3
NULL
Povezana lista na slici sadrži 4 čvora, pri čemu svaki, osim poslednjeg, sadrži pokazivač na slijededi čvor.
Poslednji čvor umjesto pokazivača na slijededi čvor, sadrži vezu sa specijalnom vrijednošdu NULL, što
označava kraj liste.
Postoji još jedan specijalni pokazivač, Start, koji pokazuje na prvi čvor u lancu.
struct cvor
{ char ime[20]; // ime do 20 karaktera
int godine; // godine starosti
float visina; // u metrima
cvor *slijedeci; // Pointer na slijedeći čvor
};
Važan dio ove structure je zadnja linija definicije structure. Ovdje definišemo članicu strukture
slijedeci koja je pointer tipa cvor, tj. strukture koju ovim definišemo. Ovo je jedini slučaj kada je
dozvoljeno da upudujemo na tip podataka kojeg još nisamo definisali (u ovom slučaju cvor).
cvor *start_ptr;
Prethodnom naredbom deklarišemo pointer start_ptr koji de sadržati pokazivač na prvi čvor liste. Na
početku, kada je lista prazna, tj. nema čvorova, u ovaj pointer postavljamo NULL vrijednost.
Najprije deklarišemo promjenljivu tipa struktura cvor i rezervišemo memorijski prostor za čvor,
slijededom naredbom:
novi
nedefinisano
U zadnjoj liniji postavljamo NULL vrijednost u članicu strukuture slijedeci, čime iniciramo da de ovaj
čvor, kada ga ubacimo u listu, biti zadnji čvor.
Sada postavljamo vrijednost za pointer start_ptr. Ako je lista prazna, onda samo adresu tmp čvora
podešavamo da pokazuje na novi čvor:
if (start_ptr == NULL)
start_ptr = novi;
Ako u listi ved postoje čvorovi, koristimo drugi pointer, pom, i pomjeramo ga na kraj liste:
{
pom = start_ptr;
// postavljamo pom na kraj liste
while (pom->slijedeci != NULL)
{
pom = pom->slijedeci; // pomjeranje na slijedeći čvor
}
pom->slijedeci = novi; // pokazivač zadnjeg čvora u listi
//postavljamo da pokazuje na novi čvor
}
Petlja de se završiti kada pom sadrži adresu zadnjeg čvora u lancu. U zadnjem čvoru liste slijedeci sadrži
null vrijednost. Kada smo pronašli zadnji čvor, postavljamo slijedeci zadnjeg čvora da pokazuje na čvor
kojeg dodajemo u listu:
pom->slijedeci = novi;
novi
čvor
dodan
NULL
void dodaj_cvor_na_kraj ()
{ cvor *novi, *pom; // pomoćni pokazivači
// Rezervisanje prostora za novi čvor i unos podataka
novi = new cvor;
cout << "Upisite ime: ";
cin >> novi->ime;
cout << "Upisite godine: ";
cin >> novi->godine;
cout << "Upisite visinu osobe: ";
cin >> novi->visina;
novi->slijedeci = NULL;
// Ubacivanje čvora u listu
if (start_ptr == NULL)
start_ptr = novi;
else
{
pom = start_ptr;
// postavljamo pom na pocetak liste
while (pom->slijedeci != NULL)
{
pom = pom->slijedeci; // pomjeranje na slijedeći čvor
}
pom->slijedeci = novi; // pokazivač zadnjeg čvora u listi postavljamo da pokazuje na novi čvor
}
return;
}
void dodaj_cvor_na_pocetak ()
{ cvor *novi, *pom; // pomocni pokazivaci
// Rezervisanje prostora za novi cvor i unos podataka
novi = new cvor;
cout << "Upisite ime: ";
cin >> novi->ime;
cout << "Upisite godine: ";
cin >> novi->godine;
cout << "Upisite visinu osobe: ";
cin >> novi->visina;
novi->slijedeci = NULL;
// Ubacivanje cvora u listu
if (start_ptr == NULL)
start_ptr = novi;
else
{
novi->slijedeci = start_ptr;
start_ptr = novi;
}
return;
}
Pomodni pointer postavljamo da pokazuje na prvi čvor, tj. dodjeljujemo mu vrijednosrt startnog
pointera (start_ptr).
Ako pomodni pointer sadrži null vriiednost, lista je prazna pa prikazujemo poruku: "Kraj liste" i
zaustavljamo prikaz.
Ako pomodni pointer ne sadrži null vrijednost, prikazujemo detalje iz čvora na koji pokazuje
pomodni pointer.
Mijenjamo vrijednost pomocnog pointera, dodjeljujudi mu vrijednost iz pointera “slijedeci”
čvora čiji smo sadržaj upravo ispisali.
Vradamo se na drugi korak.
Slijedi kod za ispisvanje podataka iz liste:
void ispisi_listu()
{
cvor *pom = start_ptr;
do
{
if (pom == NULL)
cout << endl<<"Kraj liste" << endl;
else
{ // Ispis detalja iz čvora na koji pokazuje pom pointer
cout << "Ime: " << pom->ime << endl;
cout << "Godina: " << pom->godine << endl;
cout << "Visina: " << pom->visina << endl;
cout << endl;
// Pomjeranje na slijedeći čvor
pom = pom->slijedeci;
}
}
while (pom != NULL);
return;
Brisanje čvora
Čvor može da se nalazi na početku, kraju i u sredini liste. Ovisno o njegovoj poziciji, postupak brisanja je
drugačiji.
Kada obrišemo čvor, potrebno je osloboditi memorijski prostor. Ako to ne uradimo, rizikujemo “out of
memory”! Oslobađanje memorijskog prostora realizujemo naredbom delete:
delete pom;
Međutim, ne možemo samo obrisati čvor iz liste, jer bismo u tom slučaju prekinuli lanac. Stoga, najprije
moramo izmjeniti vrijednost odgovarajudeg pokazivača pa onda obrisati čvor.
start_ptr
etc.
pom
Nakon što smo sačuvali vrijednost startnog pointera u pom pointer, mijenjamo vrijednost start_ptr tako
da pokazuje na slijededi čvor:
start_ptr
etc.
pom
start_ptr
etc.
pom
void brisi_prvi_cvor()
{ cvor *pom;
pom = start_ptr;
start_ptr = start_ptr->slijedeci;
delete pom;
return;
Brisanje čvora na kraju liste je teže, jer se pomodni pointer mora najprije pomjeriti na kraj liste (kao kod
unosa novog čvora). Potrebna su dva pomodna pointera, pom1 i pom2. Prvi de pokazivati na zadnji čvor
u listi, a drugi na njegovo prethodnika. Potrebne su nam obje vrijednosti kako bismo zadnji obrisali, a
predzadnji modifikovali tako da on u polju slijededi sadrži null vrijednost, pokazujudi na taj način da je
zadnji čvor liste.
Prikažimo proces brisanja zadnjeg čvora crtežima. Posmatrajmo listu. Najprije demo postaviti oba
pomodna pokazivača, pom1 i pom2, da pokazuju na početak liste.
start_ptr
NULL
pom1 pom2
start_ptr
NULL
pom2 pom1
Pošto pom1 još uvijek ne pokazuje na zadnji čvor u listi, pomjeramo i pom2, tako da pokazuje isti čvor
kao i pom1
start_ptr
NULL
pom2 pom1
start_ptr
NULL
pom2 pom1
Ovo ponavljamo sve dok pom1 ne pokazuje zadnji čvor u listi, a pom2 na njegovog prethodnika:
start_ptr
NULL
pom2 pom1
Zatim brišemo čvor na kojeg pokazuje pom1
start_ptr
pom2 pom1
Zatim u pokazivač slijedeci čvora na kojeg pokazuje pom2 upisujemo NULL vrijednost:
start_ptr
NULL
pom2
Ako lista sadrži samo jedan čvor, onda samo treba postaviti start_ptr na null vrijednost i obrisati čvor.
void brisi_zadnji_cvor()
{
cvor *pom1, *pom2;
if (start_ptr == NULL)
cout << "Lista je prazna!" << endl;
else
{
pom1 = start_ptr;
if (pom1->slijedeci == NULL)
{
delete pom1;
start_ptr = NULL;
}
else
{
while (pom1->slijedeci != NULL)
{
pom2 = pom1;
pom1 = pom1->slijedeci;
}
delete pom1;
pom2->slijedeci = NULL;
}
}
return;
Deklarišimo pointer tekuci. Najprije mu dodjeljujemo adresu prvog čvora u listi, koju čuvamo u pointeru
start_ptr :
cvor *tekuci;
tekuci = start_ptr;
start tekuci
itd
Pomjeranje pointera tekuci unaprijed realizujemo naredbom u kojoj pointeru tekuci dodjeljujemo
adresu cvora koji slijedi, a koja je upisana u članicu slijedeci čvora na kojeg tekuci pokazuje:
tekuci = tekuci->slijedeci;
Prije prethodne naredbe dodjele, potrebno je provjeriti da tekući u polju slijededi ne sadrži null
vrijednost. Ako je to tako, onda tekuci pokazuje na zadnji čvor u list ii nema potrebe za njegovim
pomjeranjem.
if (tekuci->slijedeci == NULL)
cout << "Pozicionirani ste na kraj liste." << endl;
else
tekuci = tekuci->slijedeci;
Pomjeranje pointera unazad je nešto teža rutina. Jedini način da nađemo prethodni čvor jeste da
krenemo od početnog i pomjeramo pokazivač do čvora kojeg tražimo. To de se desiti kada pointer
slijedeci pokazuje na tekudi čvor.
Pomod!!!
Pokušajmo sa slikama:
prethodni slijedeci
Najprije demo provjeriti da li pointer tekuci (na slici current) pokazuje na prvi čvor. Ako je tako, onda on
nema prethodnika. Ako nije, onda provjeravamo sve čvorove dok ne naiđemo na prethodnika tekudeg.
if (tekuci == start_ptr)
cout << "Pocetak liste!" << endl;
else
{ cvor *prethodni; // pointer koji pokazuje na prethodnika
prethodni = start_ptr;
Sada kada možemo da se “kredemo” unaprijed i unazad kroz listu, možemo uraditi još ponešto sa listom.
Naprimjer, možemo mijenjati podatke u čvoru liste na koji pokazuje tekuci:
Najprije, podesimo da pomodni pokazivač pokazuje na čvor iza tekudeg, tj. na čvor kojeg demo obrisati:
tekuci pom
Zatim podešavamo da pointer u tekudem čvoru sadrži adresu čvora koji slijedi iza onog na kojeg
pokazuje pomodni:
tekuci pom
Slijedi kod za brisanje čvora iza tekudeg. Ovaj kod uključuje i provjeru da li je tekudi zadnji u listi.
if (tekuci->slijedeci == NULL)
cout << "Nema vise cvorova u listi" << endl;
else
{ cvor *pom;
pom =tekuci->slijedeci;
tekuci->slijedeci = pom->slijedeci;
delete pom;
}
if (tekuci->slijedeci == NULL)
dodaj_cvor_na_kraj();
else
{ cvor *novi;
new novi;
upisi_podatke_u_cvor(novi);
U slijededem primjeru primjenidemo ove procedure i pokazati kako se implementiraju i neke dodatne
funkcionalnosti povezane liste.
Primjer 1:
Napisati program koji omogučava formiranje liste koja sadrži koeficijente kvadratne jednačine. Nakon
startovanja programa, prikazati glavni meni sa slijededim opcijama:
Opcija 5, „Sekvencijalna obrada liste“, nudi dodatni meni za pojedinačnu obradu čvorova liste.
Omogudava sekvencijalni prolazak kroz listu, gdje se za svaki čvor nude slijedede opcije:
Svaka od opcija glavnog menija i menija za sekvencijalnu obradu podataka realizuje se kao zasebna
funkcija. I sam meni komandi je relizovan kao funkcija koja se poziva u funkciji main, kao na screenshot-
u koji slijedi.
Glavni meni poziva se u funkciji main, gdje se, nakon povratka iz menija, u okviru switch naredbe poziva
odgovarajuda funkcija:
Glavni meni implementiran je na slijededi način:
Dodavanje čvora na početak liste ved je objašnjeno:
Kako bi program bio funkcionalan, samo kreiranje novog čvora realizovano je u zasebnoj, pomodnoj
funkciji „kreiraj_novi_cvor“ koja se poziva u prethodnoj, ali i u okviru drugih funkcija programa.
Ova funkcija vrada pokazivač na čvor. Kao tip povratne vrijednosti navodimo strukturu „cvor“, a ispred
imena funkcije znak *, kako bismo odredili da funkcija vrada pokazivač na čvor.
Prednost ovakvog pristupa je što istu funkciju možemo koristiti na više mjesta u programu. S druge
strane, prednost je i u tome što istu metodologiju možemo koristiti i u drugim zadacima – jedino što
treba da prilagodima je struktura čvora u funkciji kreiraj_novi_cvor (isto i u ispisi_cvor).
Za potrebe ispisivanja liste također koristimo pomodnu funkciju za ispisivanje sadržaja jednog čvora.
Argument ove funkcije je pokazivač sa fiktivnim imenom „tekuci“. On pokazuje na čvor čiji sadržaj želimo
ispisati .
Funkciju „ispisi_cvor“ koristimo u funkciji za ispisivanje liste od prvog do zadnjeg čvora, čiji algoritam
smo ved objasnili.
Funkciju „ispisi_cvor“ koristimo i u funkciji za ispisivanje liste od zadnjeg ka prvom čvoru. Ova funkcija
radi na slijededi način. Ako lista nije prazna i ako ne sadrži samo jedan čvor, koristimo dva pomodna
pokazivača. Pom1 na početku funkcije pomjerimo na zadnji čvor u petlji i ispišemo njegov sadržaj:
Sve dok pom1 ne bude imao istu adresu kao i start_ptr, radimo slijedede:
Brisanje čvora na početku i kraju liste također je objašnjeno ranije. Slijedi kod ovih funkcija:
Sekvencijalna obrada liste realizuje se pomodu funkcije „sekvencijalna_obrada“. U okviru ove funkcije
prolazimo kroz listu, od prvog do zadnjeg čvora, tako što mijenjamo vrijednost pokazovača „tekuci“.
Nakon svakog pomjeranja, prikazujemo sadržaj čvora i prikazujemo pomodni meni iz kojeg korisnik bira
odgovarajudu akciju.
Koristi se i pomodni pokazivač „pom“ u kojeg spremimo kopiju vrijednosti polja „slijedeci“ iz tekudeg
čvora. Ova vrijednost (adresa slijededeg čvora) nam je potrebna u slučaju da korisnik izabere brisanje
tekudeg čvora. Ako ne bismo imali kopiju adrese slijededeg čvora, nakon brisanja tekudeg čvora lista bi
bila prekinuta i ne bismo se mogli referencirati na slijededi čvor (koji nakon izvršene operacije postaje
tekudi).
Za svaki čvor iz liste prikazuje se pomodni meni sa opcijama rasploživim nad odabranim čvorom:
Ista funkcija sadrži switch naredbu u okviru koje pozivamo odgovarajudu funkciju za sekvencijalnu
obradu:
Funkcija za izmjenu tekudeg čvora jednostavno mijenja upisane podatke:
Dodavanje čvora prije tekudeg realizuje se tako što se najprije provjeri dali je tekudi prvi čvor liste. Ako
jeste, pozivamo funkciju za dodavanje čvora na početak liste. Ako tekudi nije prvi čvor, onda:
novi
prethodni novi
Brisanje čvora poslije tekudeg realizuje se u slijededoj funkciji:
Podaci iz jednog čvora upisuju se u jedan red sa razmacima između pojedinih vrijednosti. Osim za prvi
čvor, prije upisa podataka iz čvora, u datoteku se upisuje znak za novi red:
Zadaci za vježbu
1. Napisati program koji omogudava unos podataka o proizvoljnom broju učenika. Za svakog
učenika treba upisati slijedede podatke:
a. Redni broj, cio broj
b. Prezime i ime, string
c. Broj bodova, cio broj
d. Ocjena, cio broj (1-5)
Nakon unosa podataka o jednom učeniku, provjeriti da li je potreban nastavak unosa, tj.
Postaviti pitanje: „Nastavak? (D/N)“.
Ispisati učitane podatke, tako što podatke o jednom učeniku ispisujemo u jednoj liniji. Koristiti
povezanu listu.
2. Modifikovati zadatak 1 dodavanjem menija iz kojeg korisnik može izarbrati slijedede opcije:
a. Unos novog čvora, sa podacima redni broj i prezime i ime
i. Na početak liste
ii. Unutar liste, sa ažuriranjem postojedih rednih brojeva
1. Prije zadanog rednog broja
2. Poslije zadanog rednog broja
iii. Na kraj liste
b. Unos broja bodova:
i. za sve ucenike u listi
ii. Za odabranog učenika, na osnovu rednog broja
c. Brisanje učenika sa zadanim rednim brojem
d. Unos kriterija za ocjenjivanje u formi
i. Ocjena 1: do ___ bodova
ii. Ocjena 2: od ____ do ____ bodova
iii. Ocjena 3: od ____ do ____ bodova
iv. Ocjena 4: od ____ do ____ bodova
v. Ocjena 5: od ____ do ____ bodova
e. Ispisivanje ocjena učenika
i. Svi učenici
ii. Odabrani učenik, zadavanjem rednog broja
f. Ispisivanje rang liste po broju bodova
g. Upisivanje liste u datoteku
h. Učitavanje liste iz datoteke
3. Napisati program koji omogučava unos podataka u povezanu listu. Svaki čvor sadrži prezime i
me učenika i broj osvojenih bodova. Prilikom unosa učenika, automatski se vrši sortiranje
podataka u opadajču listu pr broju osvojenih bodova. Nakon unosa ispisati listu sa kolonama:
prezime ii me, broj bodova.
4. Napisati program koji nudi iste opcije kao i primjer 1, samo što se u čvorovima liste čuvaju
podaci o vrijednostim tri otpora R1, R2 i R3. Prilikom ispisa podataka iz liste izračunava se i
ispisuje ukupan otpor ako su otpornici spojeni serijski i paralelno.