Labori materjal
- Slaidid: Ahelloendid
Laboriülesanded
Selles laboris on üks laboriülesanne, mida laiendab 2 edasijõudnute ülesannet
Laboriülesanne: Ahelloend
Baasülesandes tuleb sul luua rakendus, mis koostab ahelloendi, väljastab selle ning võimaldab loendist otsinguid teostada.
Lae alla aluskood: https://blue.pri.ee/ttu/files/iax0584/aluskoodid/12_ll_basecode.zip
Nõuded
- Loo menüüprogramm, mis kasutab ühesuunalist ahelloendit
- Lisatavad elemendid tuleb paigutada loendi algusesse.
- Iga elemendi sees on täisarv (id) ja sõne (nimi)
- id on automaatselt genereeritud, unikaalne, järjestikuline täisarv, mis algab nullist.
- Nimi on sõne, mille sisestab kasutaja uue elemendi loomise käigus
- Nime jaoks rakenda dünaamilist mälujaotust vastavalt nime sisestatava pikkusele.
- Ülesande lahendamise käigus tuleb sul luua minimaalselt järgnevad funktsioonid: PrintNode() , CreateNode() , InsertNode() , FindNodeByID() , FindNodeByName() ja Unload() .
- Lahendus tuleb luua etteantud aluskoodi peale.
- Lahendust luues järgi samm-sammult järgnevat juhendit. Loo funktsioonid vastavalt seal kirjeldatud nõuetele.
Samm-sammuline juhend
NB! Märka, et struktuuri deklareerides on loodud andmetüübile kaks erinevat nime – struct node ja list . Mõlemad tähendavad sama asja – st on aliased (nimekaimud) tänu tüübidefinitsioonile. Kasutuse poole pealt on funktsioonid koostatud sedasi, et struct node viitab ühele elemendile, samal ajal kui list viitab loendile kui tervikule.
1 |
void PrintNode(list *pNode); |
Kontrolli, et funktsiooni poleks edastatud NULL-viita. NULL-viida puhul väljasta veateade, kehtiva viida puhul väljasta elemendi sisu.
Kommenteeri sisse testimise esimene osa (stage 1) ja jooksuta oma programmi!
NB! Testjuhtudes on sõned kirjutatud UTF-8 vormingus, mis töötab väga kenasti Linuxis, kuid võib tekitada veidi lahkhelisid Windowsi keskkonnas. Probleemide korral muuda sõnesid.
NB! See funktsioon on juba sinu eest valmis tehtud. Pööra tähelepanu funktsioonis oleva tsükli ülesehitusele! Sul on seda vaja loendi läbikäiguks järgnevate funktsioonide lahendamisel.
1 |
void PrintList(list *pHead); |
Selle funktsiooni eesmärgiks on väljastada kõigi ahelloendis olevate elementide sisu. Funktsioonile PrintList() edastatakse ahelloendi esimese liikme mäluaadress ning tsükli käigus liigutakse läbi ahelloendi kuni jõutakse loendi lõppu, mida tähistab NULL -viit.
Funktsioonile võib edastada ka NULL -viida. See juhtub tavaliselt siis, kui loend mida väljastatakse osutub tühjaks.
Funktsioon ise väljastust ei teosta, vaid kasutab selle jaoks eelmises etapis loodud PrintNode() funktsiooni.
Kommenteeri sisse testimise teine osa (stage 2) ja jooksuta oma programmi!
1 |
struct node *CreateNode(char *name); |
- Loo ja anna mälu uuele elemendile
- Määra ära ID väärtus (järjestikuline suurenev täisarv, kasuta static märksõna)
- Küsi mälu ja kopeeri struktuuri nimi
- Väärtusta pNext viit
- Tagasta struktuur
NB! Siin funktsioonis on kahel korral vaja mälu anda. Mõlemat tagastust tuleb kontrollida! Vea korral tagasta NULL-viit! Programm peab vea korral jätkama töötamist.
Kommenteeri sisse testimise kolmas osa (stage 3) ja jooksuta oma programmi! Ainukesed visuaalselt nähtavad erinevused on aadressiruumi muutus elementidel (läksime funktsioonide pinust kuhja). Valgrindiga käivitades näed mälu küsimiste arvu suurenemist!
1 |
void InsertNode(list **ppHead, struct node *pNode); |
Baasülesandes lisame uue elemendi loendi algusesse. Selleks on vaja ainult 2 omistuslauset kirjutada!
NB! Enne lisamist kontrolli, et pNode poleks NULL -viit!
Kommenteeri sisse testimise neljas osa (stage 4) ja jooksuta oma programmi! Visuaalselt väljundis midagi ei muutu võrreldes eelmise korraga.
1 |
void Unload(list *pHead); |
Seda funktsiooni kutsutakse hävitajaks (destructor), mille eesmärk on andmestruktuur mälust vabastada. Vabastamise käigus vabastatakse ka struktuuri liikmed, mis on dünaamiliselt loodud – meie puhul on selleks nimi.
Ahelloendi vabastamiseks on vaja tsüklis käia läbi kogu loend. Läbikäigu ideega tutvu PrintList() funktsiooniss.
Loendi vabastamise juures on oluline pöörata tähelepanu praeguse elemendi vabastamisele. Nimelt pärast praeguse elemendi vabastamist ei ole meil enam ligipääsu selle pNext viidale. Sellest tingitult on meil vaja vabastamiseks täiendavat abiviita praegusele elemendile, mis teeb selle protseduuri natuke keerukamaks.
Tsüklis on 3 olulist sammu. Kõige elegantsema tsükli saad kasutades while() tüüpi tsüklit!
- Praeguse elemendi aadressist koopia tegemine (seda koopiat kasutame vabastamiseks)
- Järgmise elemendi juurde liikumine ( pCurrent = pCurrent->pNext )
- Praeguse elemendi ja selle liikmete vabastamine.
Kommenteeri sisse testimise viies osa (stage 5). Jooksuta programmi kasutades valgrindi! Peaksid nägema, et rakendus on 6 korda mälu küsinud ning vabastanud.
1 |
struct node *FindNodeByName(list *pHead, char *name); |
1 |
struct node *FindNodeByID(list *pHead, int id); |
Kasuta PrintNode() funktsiooni leitud elemendi väljastamiseks.
Kommenteeri sisse testimise viiesosa (kuues 6) ja jooksuta oma programmi! Märka, et näidisväljundis on veateade väljastatud PrintNode() poolt ja ei ole seotud otsinguga!
Palju õnne! Kõik baasfunktsionaalsuseks vajaminevad funktsioonid on nüüd valmis! Nüüd lisa abifunktsioonid ja koodilaused, et rakendus oleks kasutatav läbi menüü. Testimiseks mõeldud koodi võid soovi korral oma rakendusest kustutada.
Lisada tuleks näiteks nime küsimine uue kirje loomisel või otsitava sisestused otsingufunktsioonide kasutamiseks ning vajadusel tagastuste töötlemiseks. Kui valmis, anna märku tunnitöö esitamiseks!
Testimine
Siin on näha täielikult valmis programmi väljund.
Edasijõudnute ülesanne 1: Tähestikulises järjestikus sorteeritud loend
Selle ülesandega muudame oma elementide lisamise funktsiooni selliseks, et loend oleks alati sorteeritud tähestikulises järjekorras
Nõuded
- Uuenda
InsertNode() funktsiooni sedasi, et uus element paigutataks olemasolevate vahele sedasi, et loend oleks tähestikulises järjekorras
- Selleks pead toetama elemendi lisamist loendi algusesse, lõppu või keskele
- Uuenda FindNodeByName() funktsiooni, et see lõikaks kasu tähestikulisest järjekorrast.
- Kui oled varasemalt lahendanud edasijõudnute ülesande 2, siis uuenda ka eemaldamise funktsiooni, et ka see lõikaks kasu tähestikulisest järjekorrast.
Märkused
- Vastavalt loendi asukohale kuhu element lisatakse viitasid käidelda erinevalt.
- Veendu asukohas enne lisamise alustamist!
Edasijõudnute ülesanne 2: Elementide eemaldamine
Loo kaks funktsiooni elementide eemaldamiseks loendist vastavalt nime või ID alusel.
Nõuded
- Realiseeri RemoveNodeByName() funktisoon, mis eemaldab elemendi nimelise vaste korral.
- Realiseeri RemoveNodeByID() funktsioon, mis eemaldab elemendi ID vaste korra.
- Võimalusel kasuta ära omadust, et loend on juba sorteeritud tähestikulises järjekorras (edasijõudnute ülesanne 1)
- Veendu loendi terviklikkuses pärast elemendi eemaldamist. Testi loendi keskelt, algusest ja lõpust!
Märkused
- Esmalt leia kas element üldsegi eksisteerib mida peaksid eemaldama
- Eemaldamisel ühesuunalisest loendist on sul vaja meeles pidada eelmise elemendi asukohta! Seetõttu ei saa sa korduvkasutada FindNodeBy() funktsioone! (Saaksid, kui see oleks kahesuunaline ahelloend).
- Algusest, keskelt ja lõpust eemaldamine on erinev! Kontrolli positsiooni enne eemaldamise alustamist, seejärel vali sobilik eemaldamise meetod.
- Ära unusta mälu vabastada!
- Topeltviit on vajalik loendi algusest eemaldamiseks.