A programozás alapjai Rekurzió. előadás Híradástechnikai Tanszék - preorder (gyökér bal gyerek jobb gyerek) mentés - visszaállítás - inorder (bal gyerek gyökér jobb gyerek) rendezés 4 5 6 4 6 7 5 7 - posztorder (bal gyerek jobb gyerek gyökér) képlet kiértékelése 7 4 6 5 typedef adat; typedef stuct fa { adat a; struct fa *b,*j; faelem; void feldolgoz (adat d) {
- preorder void bejar(faelem *p) void bejar(faelem *p) { { if (p) feldolgoz(p->a); { if (p->b) bejar(p->b); feldolgoz(p->a); if (p->j) bejar(p->j); bejar(p->b); bejar(p->j); - inorder void bejar(faelem *p) void bejar(faelem *p) { { if (p) if (p->b) bejar(p->b); { feldolgoz(p->a); bejar(p->b); if (p->j) bejar(p->j); feldolgoz(p->a); bejar(p->j); - posztorder void bejar(faelem *p) void bejar(faelem *p) { { if (p) if (p->b) bejar(p->b); { if (p->j) bejar(p->j); bejar(p->b); feldolgoz(p->a); bejar(p->j); feldolgoz(p->a); Közvetlen rekurzió: egy szegmens hivatkozik önmagára Közvetett rekurzió: szegmensek egymásra hivatkozásaiban kör van A rekurzió célja: A feladat méretének, vagy bonyolultságának csökkentése egy kezelhető szintig.
A rekurzió kritikus pontja: A leállás feltételének teljesülését minden esetben biztosítani kell. A rekurzió nagy előnye az elegancia Néhány sorban, könnyen érthető kódot írhatunk. A rekurzió nagy hátránya a csábítás Akkor is használjuk, ha kevéssé hatékony, sőt pazarló. Mikor ne használjunk rekurziót? Ha az eredmény zárt alakban is előállítható. Pl.: számtani sorozat n-edik eleme Ha a feladat ciklusszervezéssel is könnyen megoldható. Pl.: faktoriális számítás A rekurzió és a ciklus kapcsolata Minden ciklus megvalósítható rekurzióval. Minden rekurzió megvalósítható ciklussal és segédváltozókkal. Minden ciklus megvalósítható rekurzióval. A B C Vége A B C A Vége
Minden rekurzió megvalósítható ciklussal és segédváltozókkal. Egy könnyű probléma: A Fibonacci számsorozat A megoldásra rátalálni néha nem könnyű feladat! 5 8 4 55 Lássunk példát erre is, arra is! A sorozat harmadik elemétől kezdve minden elem az előző kettő összege. A Fibonacci számsorozat Rekurzív megvalósítás fib(unsigned n) { if (n<) return n; return fib(n-) + fib(n-); A Fibonacci számsorozat Megvalósítás ciklussal fib(unsigned n) { int regi=,uj=; for(n--;n;n--) { uj+=regi; regi=uj-regi; return uj; Egy nehéz probléma: Hanoi tornyai A MESE Hanoi tornyai Rekurzív megvalósítás void hanoi(unsigned hanyat, char honnan,char hova) { char seged=* B -honnan-hova; if (--hanyat) hanoi(hanyat,honnan,seged); printf( %c %c\n,honnan,hova); if (hanyat) hanoi(hanyat,seged,hova); 4
4 Hanoi tornyai Megvalósítás ciklussal? 4 4 4 5 6 7 8 9 4 5 5
6
A páratlan lépéseket a legkisebb koronggal tesszük meg, mindig ugyanarra. A párosadik lépések is egyértelműen meghatározottak: - Tudjuk, hogy merre kell lépni. - Amivel léptünk, azzal nem léphetünk. - A legkisebb korongra nem rakodhatunk. Hanoi tornyai Megvalósítás ciklussal - Ha n korong van, n - lépés kell. - Minden páratlanadik lépést a legkisebb koronggal kell megtenni. - Ha n páratlan, A-B-C-A sorrendben lép, ha n páros, A-C-B-A sorrendben lép. - Ha a következő lépést ugyanarra kell megtenni, oda kell lépni, ahonnan léptünk. - Ha a következő lépést ellenkező irányban kell megtenni, onnan kell lépni, ahonnan léptünk. Hogyan lehet megvalósítani a rekurziót? Hogyan lehet megvalósítani, hogy a függvénynek egyszerre több példánya éljen? Ezt már tudjuk! 7
A nagy ÖTLET: CSAK EGY VEREM KELL! Kalkulátor verem = ahol a kifejezések kiértékelése történik Hívás verem = ahol a visszatérési címet tároljuk Paraméter verem = ahol a paramétereket adjuk át Ha már lúd, legyen kövér! Tároljuk itt a szegmensek belső változóit is! Mit kell tudnia a függvényről a fordítóprogramnak, hogy meg tudja hívni? azonosítóját a megvalósító szubrutin címét paraméterezését az átadandó paraméterek típusát Ez képezi a függvény deklarációját Mit nem szükséges tudnia a függvényről a fordítóprogramnak, hogy meg tudja hívni? 8
megvalósítását az végrehajtandó utasításokat Ez képezi a függvény definícióját Hogyan definiálunk függvényt? Ezt is tudjuk! Függvény definíciója <visszatérési érték típusa> <függvény azonosítója> (<formális paraméterek listája>) <blokk> A közvetlen rekurzióhoz ez több mint elég! 9
Közvetlen rekurzió: egy szegmens hivatkozik önmagára De mi a helyzet a közvetett rekurzióval? Közvetett rekurzió: szegmensek egymásra hivatkozásaiban kör van Ez a tyúk vagy a tojás esete! Függvény definíciója Megoldás: Válasszuk szét a függvény deklarációját és definícióját! <visszatérési érték típusa> <függvény azonosítója> (<formális paraméterek listája>) <blokk>
Függvény deklarációja <visszatérési érték típusa> <függvény azonosítója> (<paraméterek listája>) ; A blokk helyén ; áll. Függvény deklarációja <visszatérési érték típusa> <függvény azonosítója> (<paraméterek listája>) ; A paraméterek nevét a fordítóprogram nem veszi figyelembe, ezért elhagyható, vagy tetszőleges nevet használhatunk. A függvény deklarációja minden a híváshoz szükséges információt tartalmaz. A függvény prototípusának is nevezzük. Másként a függvény fejléce. A függvények tetszőleges sorrendben hívhatják egymást, ha a fejléceket kigyűjtjük, és a kód elejére írjuk. Jó szokás egy fájlba írni, és #include direktívával hivatkozni rá.