Rendezések
A rendezés a legalapvetőbb programozási probléma. Van néhány adatunk és ezeket valamilyen szempont szerint nagyság szerinti sorrendbe kell rendeznünk.
Buborék
Az algoritmus alkalmazásának feltétele hogy a sorozat elemeihez létezzen egy rendezési reláció.
Az algoritmus újra és újra végigiterál a listán, összehasonlítja a lista szomszédos elemeit, és ha azok az elvárt rendezéshez képest fordítva vannak, akkor megcseréli őket. Első menetben a lista elejéről indul és a végéig megy. Ennek az első menetnek az eredményeként a legnagyobb elem (mint egy buborék) felszáll a lista végére. Így a következő menetben már elegendő az utolsó előtti elemig elvégezni a szomszédos elemek összehasonlítását és cseréjét. Az ezután következő menetben a lista utolsó két eleme lesz a helyén és így tovább...
Az algoritmus két egymásba ágyazott ciklusból áll. A tömb első elemének indexe az 1, elemeinek száma pedig n. Az elemek itt számok, és a reláció a > (nagyobb).
CIKLUS i = n TŐL i > 0 IG {
CIKLUS j = 0 TÓL i-1 IG {
HA TOMB[j] > TOMB[j+1] AKKOR {
CSERÉLD FEL ŐKET: TOMB[j], TOMB[j+1]
}
}
}
A futás befejezése után a tömb 1-es indexű eleme lesz a legkisebb és az n indexű a legnagyobb.
A buborékrendezés illusztrálása. Az (x, y) pont jelentése: az y érték a tömb x-edik eleme.
Az algoritmus onnan kapta a nevét, hogy először a legnagyobb elem „száll fel” a tömb utolsó helyére, utána a második legnagyobb az azt követő helyre, és így tovább, mint ahogy a buborékok szállnak felfelé egy pohárban.
Ebben a pontban csak olyan rendezésekkel foglalkozunk ahol a rendezendő adatok elférnek a központi memóriában. Feltesszük tehát, hogy az adatok egy a[ ] tömbben vannak. Az algoritmusokat lehetőség szerint függetleníteni szeretnénk a programozási nyelvektől, ezért bevezetünk két jelölést: a tömb első elemének indexe E, utolsó elemének indexe pedig U lesz.
A rendezést általában összetett objektumokon hajtjuk végre, ilyenkor a sorrendet valamelyik mező adja meg, amit szokás rendezési kulcsnak hívni. Az algoritmusainkban elrejtjük ezt a részletet és úgy fogalmazzuk meg a módszereket, hogy mindenhol az a[i] < a[j] összehasonlítást használjuk. Egy külön pontban vizsgáljuk azt, hogy hogyan lehet típus- és objektumfüggetlen rendezést írni.
Algoritmus
Ciklus i := E-től (U-1)-ig
min := i
Ciklus j := (i+1)-től U-ig
Ha a[j] < a[min] akkor min := j Elágazás vége
Ciklus vége
Csere(i, min)
Ciklus vége
A rendezés után az első helyre a legkisebb elem kerül. Tehát ha megtaláljuk a minimális elemet, becserélhetjük az első helyre. Ezután a második elemtől kezdődő résztömbben kell megkeresnünk a minimális elemet, és azt érdemes a második helyre cserélni....
A rendezés tehát futamokból fog állni. Egy futamban az i. indexű elemtől az utolsó elemig tartó rész minimumát keressük meg, és ezt az i. pozícióra cseréljük.
A tömbre úgy gondolunk, mintha két részből állna: az első i elem már rendezett résztömböt alkot, a többi elemet pedig még nem vizsgáltuk. Most következik az (i+1)-dik elem. Őt szeretnénk beilleszteni az első i közé úgy, hogy most már az első i+1 elemből álló résztömb is rendezett legyen. Ez ahhoz hasonló, mint amikor a kártyás kézben sorba teszi lapjait: balról indul és a következő lapot mindig beszúrja a helyére az addig rendezett lapok közé.
A beszúrás úgy történik, hogy amíg a vizsgált elem kisebb, mint a tőle balra álló, addig felcseréljük vele. Figyelni kell arra is, hogy nem értünk-e ki a tömb bal szélére, mert ott leáll a cserélgetés.
Algoritmus
Ciklus i:= (E+1)-től U-ig
j := i - 1
Ciklus amíg j >= E és a[j] > a[j+1]
Csere(j, j+1)
j := j - 1
Ciklus vége
Ciklus vége
Összefésülés
A tömböt két részre vágjuk (lehetőleg fele-fele arányban), a részeket külön-külön rendezzük (rekurzió!), majd a rendezett részeket összefésüljük.
Összefésülés
A rendezett tömbök első elemire állunk, és minden lépésben a két aktuális elem közül a kisebbet tesszük az eredmény tömbbe, majd annak mutatóját növeljük. Ha valamelyik részt végigolvastuk már, akkor egyszerűen a másik részből másolgatjuk az elemeket.
Algoritmus
rendez(E, U):
Ha E < U akkor
K := (E+U) / 2
rendez(E,K)
rendez(K+1,U)
összefésül(E,K,U)
Elágazás vége
összefésül(E,K,U):
tmp[E..U] : = a[E..U] // tömb másolása átmeneti tárolóhelyre
i := E; j := K+1
Ciklus id := E-től U-ig
Ha i > K akkor
a[id] := tmp[j]
j := j+1
különben Ha j > U akkor
a[id] := tmp[i]
i := i+1
különben Ha tmp[i] < tmp[j] akkor
a[id] := tmp[i]
i := i+1
különben
a[id] := tmp[j]
j := j+1
Elágazás vége
Ciklus vége
Gyorsrendezés
A gyorsrendezés az összefésülő rendezés egy lehetséges továbbfejlesztett változata. Előnye az összefésülő rendezéshez képest az, hogy nem szükséges egy második tömb az összefésülésekhez, vagyis feleakkora a memóriaigénye. Általában rekurzív módon szokás megfogalmazni.
Gondolat
A tömböt az első eleme szerint (helyben) szétválogatjuk. Ha az első elem értéke X, akkor a tömb elejére kerülnek az X-nél nem nagyobb, a végére pedig az X-nél nem kisebb elemek, és "középre" X. Ezután az X-nél kisebb és az X-nél nagyobb elemeket rekurzív rendezzük. Ez a megközelítés akkor rendez gyorsan, ha a szétválogatások "nagyjából" egyenlő részekre vágják az aktuális tömböket. A "rossz" bemeneteket úgy szokás kiküszöbölni, hogy a gyorsrendezés előtt összekeverjük a tömböt (ez lineáris algoritmussal megtehető.)
Algoritmus
Gyorsrendezés
gyorsrendez(a, E, U):
kever(a, E, U)
rendez(a, E, U)
eljárás vége
rendez(a, E, U):
Ha E < U akkor
K = szétválogat(a, E, U)
rendez(a, E, K-1)
rendez(a, K+1, U)
Elágazás vége
eljárás vége
Kupacrendezés
A kupac adatszerkezet használatával O(N log N)-es rendezés készíthető.
kupacrendez(T,N):
//kezdeti kupac felépítése
Ciklus i := N/2-től 1-ig
süllyeszt(i)
Ciklus vége
//maximális elemek helyretétele egymás után
Ciklus i:=1-től (N-1)-ig
csere(T,1,N-i+1)
süllyeszt(1)
Ciklus vége
eljárás vége
Kupac
A kupac adatszerkezet - többek között - rendezésre, illetve prioritási sorok megvalósítására használható. Ereje, hogy időben dinamikusan változó adatszerkezetben hatékonyan tudja megadni az aktuális legnagyobb (vagy legkisebb) elemet.
A kupac definíciója
A kupac egy olyan bináris fa, amely
majdnem teljesen kiegyensúlyozott, vagyis a legalsó szint kivételével minden csomópontnak van bal és jobb gyermeke, vagy nincs se bal se jobb gyermeke;
bármely csomópontban lévő elem kulcsa nagyobb (kisebb) a csomópont gyermekeiben tárolt elemek kulcsánál.
A fentiekből következik, hogy a kupac tetején (a bináris fa gyökerében) lévő elem maximális (minimális), és ez a tulajdonság rekurzívan igaz bármely rész-kupacra (a fa egy csomópontjából induló részfára).
A következő ábrán egy maximum-kupac látható, tehát egy olyan kupac, amelyben bármely elem kulcsa nagyobb vagy egyenlő, mint gyermekeinek kulcsa.
Nincsenek megjegyzések:
Megjegyzés küldése