350 likes | 469 Views
ASP auditorne vježbe. Liste i redovi ; pokaziva či na pokazivače; obilazak stabla. Creative Commons. slobodno smijete: dijeliti — umnožavati, distribuirati i javnosti priopćavati djelo remiksirati — prerađivati djelo pod sljedećim uvjetima:
E N D
ASP auditorne vježbe Listeiredovi;pokazivači na pokazivače; obilazak stabla
Creative Commons • slobodno smijete: • dijeliti — umnožavati, distribuirati i javnosti priopćavati djelo • remiksirati — prerađivati djelo • pod sljedećim uvjetima: • imenovanje. Morate priznati i označiti autorstvo djela na način kako je specificirao autor ili davatelj licence (ali ne način koji bi sugerirao da Vi ili Vaše korištenje njegova djela imate njegovu izravnu podršku). • nekomercijalno. Ovo djelo ne smijete koristiti u komercijalne svrhe. • dijeli pod istim uvjetima. Ako ovo djelo izmijenite, preoblikujete ili stvarate koristeći ga, preradu možete distribuirati samo pod licencom koja je ista ili slična ovoj. U slučaju daljnjeg korištenja ili distribuiranja morate drugima jasno dati do znanja licencne uvjete ovog djela. Najbolji način da to učinite je linkom na ovu internetsku stranicu. Od svakog od gornjih uvjeta moguće je odstupiti, ako dobijete dopuštenje nositelja autorskog prava. Ništa u ovoj licenci ne narušava ili ograničava autorova moralna prava. Dijelovi ove prezentacije preuzeti su iz pratećih materijala predavanja FER-a Sveučilišta u Zagrebu Tekst licencije preuzet je s http://creativecommons.org/.
Zadatak 1 (ZI 2012) Za red realiziran jednostruko povezanom listom napišite funkciju dodaj za dodavanje elementa u red. Prototip funkcije je: int dodaj(int element, atom **ulaz, atom **izlaz); Funkcija treba vratiti 1 ukoliko je element uspješno dodan u red, a 0 inače. Tip atom definiran je sljedećim programskim odsječkom: struct at { int element; struct at *sljed; }; typedef struct at atom;
Dodavanje elementa u red realiziran listom int DodajURed (int element, Red *red) { atom *novi; if (novi = malloc (sizeof (atom))) { novi->element = element; novi->sljed = NULL; if (red->izlaz == NULL) red->izlaz = novi; //ako je bio prazan else (red->ulaz)->sljed = novi; // inace, na kraj red->ulaz = novi; // zapamti zadnjeg return 1; } return 0; } novi red->ulaz red->izlaz Pozivni program: 52 Red red; init_red(&red); DodajURed(52, &red);
Realizacija reda listom typedef struct at atom; struct at { int element; struct at *sljed; }; atom atom element sljed typedef struct { atom *ulaz, *izlaz; } Red; problem kod realizacije poljem jest mogućnost popunjenja u stvarnosti veličina reda nije ograničena • realnija je realizacija listom
Usporedba s primjerom iz predavanja A: intDodajURed (int element, Red *red); B: int dodaj(int element, atom **ulaz, atom **izlaz); Razlike? Zašto? Informacija je ista – u (A) smo dva pokazivača „zapakirali” u strukturu Red Pored toga, strukturom možemo doći do adrese pokazivača, red->izlaz u (A) je slično kao *izlaz u (B)
Strategija rješavanja Ulazom u funkciju imamo integer, trebamo ga dodati u red očito prvo kreiramo zapis U postojeći red dodajemo novi zapis na ulaz („kraj” reda) Pokazivač na kraj reda moramo pomaknuti na novi kraj ako je ulaz adresa pokazivača kraja onda sa *ulaz na LHS (left-hand side, lijevo od znaka „=”) pokazivač na zapis Na kraju rubni uvjeti
„... očito prvo kreiramo zapis” /* .... */ atom *novi; novi = malloc (sizeof (atom)); novi->element = element; novi->sljed = NULL; /* .... */
„…U postojeći red dodajemo novi zapis na ulaz („kraj” reda)…” + „... Pokazivač na kraj reda moramo pomaknuti na novi kraj...” /* .... */ /* prvo osigurati da stari zadnji */ /* pokazuje na novog zadnjeg */ (*ulaz)->sljed = novi; /* zatim pomaknuti pointer reda na novog */ *ulaz = novi; /* .... */
„...rubni uvjeti” i ostatak lista je bila prazna: treba i pokazivač na izlaz postaviti malloc može vratiti NULL ako ne dobije traženu memoriju povratne vrijednosti iz funkcije
intdodaj(int element, atom **ulaz , atom **izlaz){ atom *novi; if (novi = malloc (sizeof (atom))) { novi->element = element; novi->sljed = NULL; if (*izlaz == NULL) *izlaz = novi; else (*ulaz)->sljed = novi; *ulaz = novi; return 1; } return 0; }
Zadatak 2 (ZI 2008.) U jednostruko povezanu listu spremaju se podaci o radnicima nekog poduzeća. Lista je zadana sljedećim strukturama. struct zapis { char imeprezime[30+1]; int matbr; int godisnji; }; struct at { struct zapis element; struct at *sljed; }; typedef struct at atom;
Zadatak 2 nastavak Jedan element liste sadrži sljedeće podatke: ime i prezime radnika (30+1 znakova), matični broj radnika (int) i broj dana godišnjeg odmora (int). Lista nije sortirana. Napisati funkciju koja će iz liste izbaciti one radnike koji imaju više od 30 dana godišnjeg odmora. Funkcija mora imati prototip: void izbaci(atom **glava);
Strategija rješavanja Negdje (zamislimo da je u main()-u) je lista bila popunjena i tamo (opet recimo u main()-u) se nalazi pokazivač „atom* adresaListe”, a sadrži adresu same liste, recimo „0x100“ Ako je prvi element liste bio izvađen i stavljen u novu listu, očito moramo u main()-u promijeniti vrijednost u pokazivaču adresaListe Zato funkciji moramo predati adresu tog pokazivača (recimo da je pokazivač na adresi „0xA”), tako da u funkciji promijenimo sadržaj te lokacije
Strategija rješavanja (2) Kretati ćemo se po listi i izbacivati neke elemente Da bismo izbacili element, treba nam adresa elementa (pokazivač na struct) Da bismo povezali prethodnika na sljedbenika, dovoljan nam je pokazivač (radimo standardnu operaciju tipa „kaskajuci = front; front = front -> slijedeci;”) Samo za glavu liste moramo imati pokazivač na pokazivač – imamo varijablu glava iz argumenta!
„ Da bismo izbacili element, treba nam adresa elementa „ void izbaci(atom **glava) { atom *stari, *prethodni; stari = *glava; /* ... */ free(stari); /* ... */
„... Kretati ćemo se po listi ...” void izbaci(atom **glava){ atom *stari, *prethodni; stari = *glava; prethodni = NULL; while(stari) { /* ... */ free(stari); /* ... */ prethodni = stari; stari = stari->sljed; }
Očito ili izbacujemo element (koji ispunjava uvjet) ili se krećemo po listi: void izbaci(atom **glava){ atom *stari, *prethodni; stari = *glava; prethodni = NULL; while(stari) { if (stari->element.godisnji > 30) { /* ... */ free(stari); /* ... */ } else { prethodni = stari; stari = stari->sljed; } }
I popunimo još što fali (rubni uvjet za praznu l.): void izbaci(atom **glava){ atom *stari, *prethodni; stari = *glava; prethodni = NULL; while(stari) { if (stari->element.godisnji > 30) { if (stari == *glava){ *glava = stari->sljed; free(stari); stari = *glava; } else { prethodni->sljed = stari->sljed; free(stari); stari = prethodni->sljed; } } else { prethodni = stari; stari = stari->sljed; } } }
Zadatak 3 (dekanski 2011.) U jednostruko povezanu nesortiranu listu spremljeni su podaci o uspjehu studenata na nekom predmetu. Lista je zadana sljedećim strukturama: struct zapis { char imeprezime[80+1]; char jmbag[10]; float ocjena; }; struct at { struct zapis element; struct at *sljed; }; typedef struct at atom;
Zadatak 3 nastavak Jedan element liste sadrži sljedeće podatke: ime i prezime studenta (80+1 znakova), JMBAG (10 znakova) i završna ocjena iz predmeta (float). Napišite funkciju koja će iz zadane liste sve čvorove koji sadrže studente koji su položili predmet prebaciti (bez stvaranja novih čvorova) u novu jednostruko povezanu listu. Funkcija treba vratiti pokazivač na glavu nove liste i prosjek ocjena studenata koji su položili predmet. Funkcija mora imati prototip: atom *razdvojiStudente(atom **glava, float *prosjek);
Zadatak 3 nastavak Napomena: U izvornoj listi na koju pokazuje glava ostaju čvorovi koji sadrže studente čija ocjena nije dovoljna za prolazak predmeta. Primjer: Varijabla glava prije poziva funkcije pokazuje na listu čvorova koji sadrže sljedeće ocjene: 5, 1, 4, 3, 4, 3, 2, 4, 5, 1. Prosjek ocjena je 3,75 (vrijednost varijable prosjek) pa nakon poziva funkcije razdvojiStudente ona vraća pokazivač na novu listu koja sadrži čvorove sa sljedećim ocjenama: 5, 4, 3, 4, 3, 2, 4, 5. Varijabla glava pokazuje na listu čvorova koji sadrže preostale zapise.
Strategija rješavanja Slično kao i u prethodnom zadatku Radimo s novom listom: moramo čuvati adresu prvog elementa (za povratnu vrijednost) i zadnjeg elementa (za dodavanje u listu) Ništa ne free()-amo, samo prebacujemo zapise (strukture) iz jedne liste u drugu – dakle samo mijenjamo vrijednosti pokazivača u strukturama!
atom *razdvojiStudente (atom **glava , float *prosjek){ float suma = 0; intbrojac = 0; atom *novi, *p, *preth, *zap = NULL; novi = NULL; for (p = *glava; p; p = p->sljed) { if (p->element.ocjena >= 2) { suma += p->element.ocjena; ++brojac; } } *prosjek = 0; if (brojac == 0) return novi; *prosjek = suma/brojac;
for (p = *glava; p; p = p->sljed) { if (p->element.ocjena >= 2) { if (novi == NULL)novi= p; elsezap->sljed = p; zap = p; if (p == *glava) { *glava = (*glava)->sljed; } else { preth->sljed = p->sljed; p = preth; } } preth = p; } if (zap) zap->sljed = NULL; return novi; }
Zadatak 4 (ZI 2008) a) Napišite funkciju čiji je prototip int jeligomila(int stablo[], int n); koja će vratiti 1 ako je potpuno binarno stablo s n elemenata (implementirano poljem) gomila, a 0 inače. b) Napišite funkciju čiji je prototip int jeligomila(cvor *korijen); koja će vratiti 1 ako je potpuno binarno stablo (čiji je korijen zadan) gomila, a 0 ako nije.
Strategija riješavanja • Moramo provjeriti da stablo ima svojstvo gomile, znači: • Korijen je veći od lijevog i od desnog djeteta • Lijevo djete je veće od svog lijevog i od svog desnog djeteta • Desno dijete je veće od svog lijevog i od svog desnog djeteta • Lijevo djete lijevog djeteta je veće... • Rekurzija? Dijelimo i vladamo
a: intjeligomila(intstablo[], int n){ inti; for (i = n; i >= 2; i--) if (stablo[i] > stablo[i/2]) return 0; return 1; }
b: intjeligomila(cvor *korijen){ if (korijen == NULL) return 1; if ((korijen->lijevo != NULL) && (korijen->lijevo->element > korijen->element)) return 0; if ((korijen->desno != NULL) && (korijen->desno->element > korijen->element)) return 0; return jeligomila(korijen->lijevo) && jeligomila(korijen->desno); }
Zadatak 5 (ZI 2012) Za tip podatka Stog koji je realiziran jednostruko povezanom listom definirane su funkcije za inicijalizaciju stoga, dodavanje elementa na stog te skidanje elementa sa stoga. Zadana su dva stoga stog1 i stog2, a elementi stogova su podaci tipa Osoba koji sadrže informaciju o šifri osobe (cijeli broj) i visini (cijeli broj). Stog stog1 sadrži podatke samo za muškarce, a stog2 samo za žene i na oba stoga su podaci sortirani po visini tako da je najviša osoba na vrhu stoga.
Zadatak 5 nastavak Prototipovi navedenih funkcija su: void init_stog(Stog *stog); int dodaj(Osoba element, Stog *stog); int skini(Osoba *element, Stog *stog); Funkcije dodaj i skini vraćaju 1 ako je operacija dodavanja ili skidanja uspjela, a 0 inače. a)(1 bod) Napišite definicije odgovarajućih struktura (tipovi podataka Osoba, Stog i atom) za stog realiziran jednostruko povezanom listom. b)(7 bodova) Napišite rekurzivnu funkciju spoji koja će sa stoga stog2 skinuti sve podatke i staviti ih na stog stog1, ali tako da ostane očuvan poredak po visini. Poredak osoba s istom visinom nije bitan.
Strategija rješavanja • Zamislimo da treba samo poskidati elemente sa stoga i vratiti ih istim redosljedom • Ako radim rekurzivno, mogu u prvom pozivu skinuti prvi element, čuvati ga lokalno i rekurzivno riješiti manji stog i nakon toga vratiti element na vrh • U svakom koraku treba skinuti veći element sa stoga • Možemo skinuti oba, zatim usporediti i vratiti manji nazad na stog s kojeg je skinut • Samo moramo paziti da ne radimo s jednim ili oba prazna stoga (kad su oba prazna je lako – to je kraj rekurzije)
typedefstruct { intsifra; intvisina; } Osoba; struct at { Osoba element; struct at *sljed; }; typedefstruct at atom; typedefstruct{ atom *vrh; } Stog;
void spoji (Stog *s1, Stog *s2){ Osobaos1, os2, pom; intimas1, imas2; imas1=skini(&os1, s1); imas2=skini(&os2, s2); if (!imas1 && !imas2) return; else if (imas1 && imas2) { if (os1.visina <os2.visina) { dodaj(os1,s1); pom=os2; } else { dodaj(os2,s2); pom=os1; } } else if (imas1) pom=os1; else pom=os2; spoji(s1,s2); dodaj(pom,s1); }