Anda di halaman 1dari 89

Adatstruktúrák

Király Zoltán
el®adásai alapján
lejegyezte: Susztár Zoltán

2005/2006-os tanév tavaszi szemesztere

2.2 verzió

2007. szeptember 25.


Tartalomjegyzék

1. Alapvet® adatszerkezetek 4
1.1. Adatstruktúrák tervezése . . . . . . . . . . . . . . . . . . . . . . 4

1.2. Láncolt lista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

1.3. Sor és verem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

1.4. Gráfok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2. Gráfok bejárása: szélességi keresés 10


2.1. Összefügg®-e a gráf ? . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.2. Gráfok komponensekre bontása . . . . . . . . . . . . . . . . . . . 12

2.3. Legrövidebb utak . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.4. Adott gráf kétszínezhet®-e ? . . . . . . . . . . . . . . . . . . . . . 15

2.5. Irányított gráfok . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3. Kupac 16

4. Minimális költség¶ feszít®fa és legrövidebb út keresése 21


4.1. Kruskal algoritmusa . . . . . . . . . . . . . . . . . . . . . . . . . 21

4.2. DISZJUNKT-UNIÓHOLVAN adatstruktúrák . . . . . . . . . . 23

4.3. Prim algoritmusa . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

4.4. Dijkstra algoritmusa . . . . . . . . . . . . . . . . . . . . . . . . . 33

4.5. d-edfokú kupacok . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

5. Amortizációs elemzés 37
5.1. Konvex burok keresése . . . . . . . . . . . . . . . . . . . . . . . . 37

5.2. Az amortizációs elemzés . . . . . . . . . . . . . . . . . . . . . . . 38

5.3. Példa : a konvex burok keresés amortizációs elemzése . . . . . . . 39

6. Fejlettebb kupacok 39
6.1. Fibonacci kupac . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

6.1.1. Amortizációs id® és a strigulázás kapcsolata . . . . . . . . 45

6.2. Párosítós kupacok . . . . . . . . . . . . . . . . . . . . . . . . . . 46

6.2.1. A párosítós kupacok legjobb változata . . . . . . . . . . . 50

2
6.3. r-kupacok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

7. Szótárak 53
7.1. Bináris keres®fa . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

7.1.1. Optimális bináris keres®fa . . . . . . . . . . . . . . . . . . 56

7.1.2. M¶veletek általános bináris keres®fában . . . . . . . . . . 58

7.2. 2-3 fák . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

7.3. B-fák . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

7.4. Piros-fekete fák . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

7.5. AVL-fák (Adelszon-Velszkij és Landisz) . . . . . . . . . . . . . . 66

7.6. Önkiegyensúlyozó fák (Splay-tree ; Sleator és Tarjan) . . . . . . . 68

8. Hashelés 71
8.1. Hash függvények . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

8.2. Ütközések feloldása . . . . . . . . . . . . . . . . . . . . . . . . . . 73

8.2.1. Láncolt hash-elés . . . . . . . . . . . . . . . . . . . . . . . 73

8.3. Nyílt címzés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

8.4. Lineáris hash-elés . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

8.5. Dupla hash-elés . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

8.6. A dupla hash-elés Brent-féle változata . . . . . . . . . . . . . . . 77

8.7. Univerzális hash-elés . . . . . . . . . . . . . . . . . . . . . . . . . 78

9. Geometriai adatstruktúrák 78
9.1. Egy dimenziós feladatok . . . . . . . . . . . . . . . . . . . . . . . 79

9.2. Két dimenziós feladatok . . . . . . . . . . . . . . . . . . . . . . . 81

9.2.1. Kaszkád tárolás . . . . . . . . . . . . . . . . . . . . . . . . 82

9.3. Egy téglalapot metsz® szakaszok lekérdezése . . . . . . . . . . . . 83

9.3.1. Kupacos keres®fák . . . . . . . . . . . . . . . . . . . . . . 84

9.4. Ferde szakaszok lekérdezése . . . . . . . . . . . . . . . . . . . . . 86

9.4.1. Szakasz-fa . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

9.4.2. Egy függ®leges szakaszt metsz® ferde szakaszok . . . . . . 88

3
1. Alapvet® adatszerkezetek

1.1. Adatstruktúrák tervezése


Programozási feladatok megoldása : A programozási feladatok általános megol-
dása során el®ször az algoritmus vázlatát készítjük el, majd az adatstruktúra
denícióját : milyen objektumokat akarunk tárolni, és ezekkel milyen m¶velete-
ket akarunk végezni.

Ezután az algoritmust fokozatosan pontosítjuk, egyre részletesebben kidol-


gozzuk. A nomítások során készítjük el az eljárásokat, amelyek el®állítása során
a fenti lépések ismétl®dnek meg. Ehhez hasonló módon deniáljuk és nomítjuk
az adatstruktúrákat is. Az, hogy milyen adatokat akarunk tárolni az adott adat-
struktúrában és milyen m¶veleteket akarunk végezni rajtuk, fogja meghatározni,
hogy milyet választunk. Az adatstruktúrák tervezésének lépései :

Specikáció :

 Milyen objektumokat akarunk tárolni

 Milyen m¶veleteket akarunk végezni ezeken az objektumokon, ezek a m¶-


veletek általában :

o Üres adatstruktúra létrehozása

o Elem berakása az adatstruktúrába

o Speciális m¶veletek (pld. a legkisebb kulcsú elem kivétele)

Ezután az adatsruktúrát az algoritmushoz ill. az eljárásokhoz hasonlóan egy-


re pontosabban megtervezzük/megvalósítjuk.

A gyakorlatban nincs univerzálisan legjobb adatstruktúra, a felhasználás


határozza meg, hogy melyik lesz a legmegfelel®bb (pld. : a gyakorlatban nem
feltétlenül az az adatstruktúra lesz a legjobb, amelyben a leglassabb m¶velet
elvégzésének ideje minimális). Azt a megvalósítást érdemes választani, ahol az
algoritmus által leggyakrabban használt m¶veletek a leggyorsabban valósíthatók
meg.

Ha rendelkezésünkre áll már egy jó adatstruktúra, akkor az is el®fordulhat,


hogy érdemesebb az algoritmuson változtatni.

1.2. Láncolt lista


Tulajdonságai :

 Az elemek a memóriában szétszórtan helyezkedhetnek el.

4
 Minden elem két részb®l áll : egy mutatóból (pointerb®l), amely a követ-
kez® elem memóriacímét mutatja, valamint egy, az adato(ka)t tartalmazó
részb®l.

 A lista els® elemére a listafej mutat, a lista utolsó elmének mutatója pedig
nem mutat sehová (nil mutató).

Kés®bb fogunk egyéb láncolt listákkal is találkozni, pl.

 kétszeresen láncolt listával, ahol egy listaelemhez két pointer tartozik, az


egyik a következ®, a másik a megel®z® listaelemre mutat,

 ciklikusan láncolt listával, ahol az utolsó elem pointere nem nil, hanem a
legels® elemre mutat,

 illetve ezek kombinációjával.

listafej

nil

1.3. Sor és verem


Tulajdonságai :
 Tetsz®leges típusú objektumok tárolására alkalmas

 Ha egy sorból kiveszünk egy elemet, akkor a legrégebben berakott ele-


met kapjuk vissza. A verem annyiban különbözik a sortól, hogy ebben
az esetben elem-kivétel esetén az utolsónak berakott objektumot kapjuk
vissza.

M¶veletek :
 Új, üres sor létrehozása (S névvel n méret¶t) : Újsor(S, n)

 Üres-e a sor? : S =?∅


 Új elem berakása a sorba : S ← v
 Elem kivétele a sorból : u ← S

M¶veletek megvalósítása : Cél, hogy konstans id®ben el tudjunk végezni


minden m¶veletet.
A sor méretét meghatározó n érték két jelentéssel bírhat :

5
1. Ha n fels® becslés az S←v m¶veletek számára :
ELEJE,VÉGE : pointer

1 n
ST 3 6 1

ELEJE
VÉGE

Újsor(S, n) :
Újtömb(ST,n)
ELEJE :=1 ; VÉGE :=0

S =?∅ : ELEJE > VÉGE

S ← v:
VÉGE++
ST(VÉGE) :=v

u ← S:
u :=ST(ELEJE)
ELEJE++

2. Ha csak n ≥ |S| teljesül mindig :

n+1 méret¶ tömbben ciklikusan oldjuk meg a m¶veleteket.

A verem megvalósítása hasonló, itt a második eset is könnyen megvalósítható


és nem kell az ELEJE mutató.

1.4. Gráfok
Cél : adatstruktúra egy V = {1,2, . . . n} csúcshalmazú gráf tárolására. (Ha a
gráfunk nem ilyen módon adott, akkor feltesszük, hogy létezik egy olyan bi-
jektív függvényünk, ami ilyen csúcshalmazúvá számozza át  és vissza  a gráf
csúcsait.)

1. Szomszédsági mátrix : n×n-es A = {ai,j } mátrixban tároljuk az éleket :



1 ha ij ∈ E
ai,j =
0 ha ij ∈
/E

6
j

i
a ij

Ha a gráf irányítatlan, akkor a mátrix szimmetrikus lesz. Ekkor a f®átló


feletti részt elég tárolni.

Tárhely : n2 (bit) (kevés programnyelv támogatja a bit mátrixot, ezért a


gyakorlatban sokszor n2 rövid egészet használunk.)

M¶veletek :

 Új (csupa-0) mátrix létrehozása


 A gráf egy élének behúzása vagy törlése : ai,j := 0
1 (általában inputként
kapjuk)

 ij ∈?E : ez itt 1 lépésben megy

 Adott i csúcsból kimen® élek felsorolása : FOR ij ∈ E . . .


Végigmegyünk a mátrix i-edik során

Megjegyzés :
Multigráf (olyan gráf, amelyben lehetnek hurokélek is, és a csúcsok közt több
párhuzamos él is mehet) :

 ai,j = k , ha i-b®l j -be k db párhuzamos él megy

 ai,i = k , ha az i-dik csúcson k darab hurokél van (vagy aii = 2k , így az


i-edik sor összege a csúcs fokszáma lesz).

Súlyozott gráfok :
Két esetet különböztetünk meg :

 A 0 súly nem megengedett : Ebben az esetben nincs probléma : a 0 érték


azt jelenti, hogy a két csúcs közt nem megy él, a nem nulla érték pedig a
két csúcsot összeköt® él súlyát jelenti.

 A 0 súly is megengedett : Ekkor több információra van szükségünk : ismer-


nünk kell például a legkisebb súlyt. (ha például a súlyok nemnegatívak,
akkor −1 jelentheti azt, hogy az adott csúcsok közt nem megy él.)

7
2. Éllista : Egy L tömbben tároljuk a csúcsokból kiinduló éleket felsoroló
láncolt listák listafejeit.

L
1

i 2 5 8 nil

n
LISTAFEJEK

Az i. csúcsból legalább 3 él megy ki : pl. a 2-es,. . . , az 5-ös és a 8-as számú


csúcsba.

Tárhely : n + e mutató (irányítatlan esetben n + 2e, mivel minden él kétszer


szerepel) ése (ill. 2e) szám.

M¶veletek :

 Üres struktúra létrehozása : A listafej-tömböt nil-ekkel töltjük fel.

 Új él beszúrása (ij irányított él beszúrása) :


p := L(i); L(i) := beszúrt új (j, p) listaelem címe a memóriában.
(Érdemes mindig a láncolt lista elejére beszúrni.) Lépésszám : O(1).
 ij ∈?E : Legrosszabb esetben végig kell menni a láncolt listán. Ez d(i) ≤
≤ n − 1 lépést jelent. (Irányított gráf esetén a fels® korlát az adott csúcsra
a dki (i) kifok.)

 Adott i csúcsból kimen® élek felsorolása : A lépésszám ebben az esetben is


d(i) vagy dki (i)
 ij él törlése : Az i-edik és a j -edik listában meg kell keresni, ez d(i)+d(j)
lépés, utána kitörölni már konstans.

Megjegyzések :
 Az élfelsorolás (általában a leggyakrabban használt) m¶velet ideje kis fok-
szám esetén sokkal jobb, mint a szomszédsági mátrixnál.

8
 Megfordított gráf generálása : végigmegyünk az éleken és egy új éllistába
átmásoljuk a fordítottjukat, ez O(e) id®ben megoldható.

 Irányított gráfból a megfelel® irányítatlan gráf generálása : végigmegyünk


az éleken és egy új éllistába átmásoljuk az éleket is és a fordítottjukat is,
ez O(e) id®ben megoldható.

 Multi- és súlyozott gráfok esetén : A láncolt listában minden gond nélkül


tárolható. Mivel egy listaelem adatrésze akárhány adatból állhat, még az
se gond, ha az élekhez több szám (kapacitás, súly és hossz) is tartozik.

3. Mátrixban tárolt éllista (egyszer¶ gráfokra)


Egy n × D méret¶ mátrixot használunk. A mátrix i-edik sorában felsoroljuk
azi-edik csúcsból kiinduló élek végpontjait. Ha k darab él megy ki, akkor a
mátrix i-edik sorában a k + 1-ik oszloptól a D -ik oszlopig nullák szerepelnek.

i 2 5 8 0 0 0
n

Az i. csúcsból 3 él megy ki : a 2-es az 5-ös és a 8-as számú csúcsba.

D megválasztása :

 Ha a fokszámokról semmit nem tudunk : D := n.


 Ha ∆= maxfok(G) ismert, akkor D := ∆ + 1 (azért érdemes ∆ + 1-et
választani, hogy minden sor 0-val végz®djön, így az i. csúcsból akkor nem
indul ki több él, ha az i. soron végigmenve 0-hoz értünk).

Nyereség :

 Nem kell pointereket használni

Veszteség :

9
 Tárhelyet veszthettünk is : a tárhely mérete n× maxfok, n× átlagfok he-
lyett (és ez még a jobb eset : ha ismerjük ∆-t, ha ∆-ról nem tudunk semmit,
akkor n × (n + 1) hely kell).

M¶veletek ideje : Ugyanaz, mint az éllista esetén, s®t, egy kicsit még gyorsabb
is, mivel nem kell pointerekkel dolgoznunk.
Összefoglalva : ez a megoldás a gyakorlatban is egy jó megoldásnak tekinthet®,
ha vagy ismerünk egy n-nél kisebb fels® korlátot ∆-ra, vagy a tárfoglalás nem
nagyon érdekel minket (hiszen az egy csúcsból kiinduló éleket itt is fel tudjuk
sorolni fokszám id®ben).

Speciális esetek Páros gráfok : elég n2


4 hely.

a f

Fák tárolása :

 Éllista

 Prüfer kód : Egy n csúcsú gráf esetén n − 2 darab szám (abban az eset-
ben, ha a gráf csúcsai 1-t®l n-ig számozottak). Ez az n − 2 darab szám
bijektíven (F fa ↔ a1 , a2 , . . . an−2 , ahol 1 ≤ ai ≤ n) írja le a fát. Ez a
tárolás nagyon alkalmas véletlen fák generálására (ami egyébként nem egy
egyszer¶ feladat).

 Pointerekkel (gyökeres fáknál alkalmazzuk, ahol a felfelé men® élet meg


akarjuk különböztetni a többit®l ; lásd kés®bb). Ha a gyerekek számára
nincs jó fels® korlát, akkor a csúcsban az els® gyerekre mutat csak pointer,
és a gyerekek egy láncolt listában vannak.

 Kupacoknál (teljesen kiegyensúlyozott fák) tömbben

2. Gráfok bejárása : szélességi keresés

Szélességi keresés esetén egy adott pillanatban minden egyes csúcs 3 féle álla-
potban lehet :

a) Az algoritmus során eddig még nem látott

b) Látott, de még nem átvizsgált

10
c) Látott és átvizsgált

Egy u csúcs átvizsgálása azt jelenti, hogy végigmegyünk a bel®le kimen®


éleken, és ha a) típusú csúcsot találunk, azt átsoroljuk b) típusúvá ; végül u-t
magát pedig c) típusúvá.
A szélességi keresésnél a b) állapotú csúcsok közül a legrégebbit vizsgáljuk
át.

Egy L bittömböt (L(i) = 1 ⇔ ha i-t már láttuk az algoritmus során) és egy


S sort ( ez fogja tartalmazni a b) állapotú csúcsokat) használunk.

Az algoritmus :
INIT :
Újsor(S, n) ; Újtömb(L, n)
FOR i := 1 . . . n L(i) := 0
L(1) := 1 ; S ← 1 (látott, de nem átvizsgált)

WHILE S 6= ∅
u←S
FOR uv ∈ E
IF L(v) = 0 THEN L(v) := 1; S ← v
Ez az algoritmus magában nem ad outputot, csak végigmegy a gráf 1-es
csúcsból elérhet® élein.

Lépésszám : Egy csúcsot maximum egyszer rakunk be a sorba, így maximum


n-szer tudunk kivenni elemet a sorból. A lépésszám így O(n2 ), ha szomszédsági
mátrixban tároljuk a gráfot.

1. Tétel. Nincs olyan algoritmus, amely tetsz®leges, szomszédsági mátrixszal


adott gráfról kevesebb, mint n2 id®ben eldönti, hogy összefügg®-e, tehát a szé-


lességi keresés lényegében optimális lépésszámú szomszédsági mátrix esetén.

Éllistás tárolás esetén a FOR ciklus d(u)


P-szor fut le. A futási id®t ekkor nem
érdemes n × max d(u)-val becsülni, hanem u (1 + d(u)) = n + 2e-vel becsüljük.
Így a futási id® O(n + e) lesz, ami izolált csúcsot nem tartalmazó gráf esetén
O(e).

2. Tétel. Ennél lényegesen gyorsabban éllistás tárolásnál sem valósítható meg


a gráfbejárás feladata.

Bizonyítás : azt állítjuk, hogy kell legalább |E|/2 input olvasás.

Minden élszámra tudunk olyan gráfot konstruálni, hogy ennél kevesebb olva-
sással ne legyen eldönthet® a gráf összefügg®sége. Legyen G1 és G2 két egyforma,

11
tetsz®leges összefügg® gráf, az inputnak a két komponense. Ha nem olvassuk vé-
gig legalább az egyiknek az összes élét, akkor G1 és G2 egy-egy nem végigvizsgált
csúcsa közé berakhatunk egy új élet (az éllistáik végére), ezt az algoritmus nem
veszi észre.

Az alábbiakban megnézünk néhány fontos feladatot, amelyeket a szélességi


keresés segítségével könnyen megoldhatunk.

2.1. Összefügg®-e a gráf ?


Ha minden 1 ≤ i ≤ n-re L(i) = 1, akkor a gráf összefügg®, különben nem.

2.2. Gráfok komponensekre bontása


A gráf egy adott komponense egy ekvivalencia osztály, ezt úgy tároljuk, hogy
tetsz®legesen elnevezzük a komponenseket, és egy csúcsra az ®t tartalmazó kom-
ponens sorszámát tároljuk (két csúcs akkor van ugyanabban a komponensben,
ha ez a szám ugyanaz). Triviális megoldás : 1-es értéket adunk az els® csoport-
nak, majd amikor a sor kiürül, akkor keresünk egy olyan w csúcsot, melyre
L(w) = 0, és egy innen indított újabb kereséssel megkeressük a 2. komponens
csúcsait, majd így tovább.

1. Állítás. Akár c · n2 id®t is elhasználhatunk csak a 0-k keresésére.

Bizonyítás : A G gráf legyen olyan, hogy az els® n/2 csúcs egy komponensbe
tartozik, majd minden ezt követ® csúcs izolált. Ekkor az els® komponens után
n/2 + 1 lépés az els® 0 megtalálása, majd n/2 + 2, és így tovább.

Hasznos észrevétel : A csúcsok látottsága csak egy irányba változik. Ha az


els® bejárásnál az i-edik csúcsnál találtunk 0-t, legközelebb elég az i + 1-dik
elemt®l kezdeni a következ® nullás keresését.

Az algoritmus :
INIT :
Újsor(S, n) ; Újtömb(L, n)
FOR i = 1...n L(i) := 0
k := 0

FOR i = 1...n
IF L(i) = 0 THEN k + + ; L(i) := k ; S ← i
WHILE S 6= ∅
u←S
FOR uv ∈ E
IF L(v) = 0 THEN L(v) := k ; S ← v

12
Lépésszám (éllistás tárolásnál) : O(n + e).

2.3. Legrövidebb utak


Feljegyezzük, hogy az egyes csúcsokat mely csúcsból láttuk meg, és minden
csúcshoz hozzárendelünk egy SZ szintet.

Az algoritmus :
INIT :
Újsor(S, n) ; Újtömb(L, n) ; Újtömb(p, n) ; Újtömb(SZ, n)
FOR i = 1...n
L(i) := 0 ; p(i) := i ; SZ(i) := 0
k := 0

FOR i = 1...n
IF L(i) = 0 THEN k + +, L(i) := k ; S ← i
WHILE S 6= ∅
u←S
FOR uv ∈ E
IF L(v) = 0 THEN L(v) := k ; S ← v ; p(v) := u
SZ(v) := SZ(u) + 1

3. Tétel. {(p(u), u)|p(u) 6= u} ⊆ E(G) egy feszít®erd® lesz. (Ha összefügg® a


gráf, akkor feszít®fa, különben minden komponensben egy feszít®fa.) Ha a gráf
irányított, akkor a gyökért®l elfelé irányított fákat (feny®ket) kapunk.

Bizonyítás (összefügg®, irányítatlan esetre) : n−1 darab él, ha nem feszít®fa


lenne, akkor lenne benne kör, ez azonban nem lehet, mert a kör csúcsai közül a
sorba leghamarabb berakottnak a szül®je nem lehet egy kés®bb berakott.

4. Tétel. Szélességi keresés esetén

1. Bármikor a sorban balról jobbra a szintszám monoton n®


2. Ha x, y ∈ S , akkor |SZ(x) − SZ(y)| ≤ 1

Bizonyítás : Id®re vonatkozó indukcióval.

 Amikor egy elem van a sorban, triviálisan teljesülnek.

 Amikor új elemet rakunk be elromlanak-e a tulajdonságok ? Tegyük fel,


hogy v berakásakor valamelyik elromlik, legyen v szül®je u, tehát SZ(u) =
= SZ(v) − 1. Vegyük észre, hogy a sor minden x elemére, ha SZ(x) 6=
= SZ(v), akkor x már a sorban volt u kivétele el®tt is.

13
1. tulajdonság :

u x v

SZ: 3 5 4

Ekkor x a v betétele el®tt is benne volt a sorban, ami ellentmond annak,


hogy u kivétele el®tt teljesült a 2. tulajdonság.

2. tulajdonság :

u x v

SZ: 4 3 5

Ekkor is xav betétele el®tt is benne volt a sorban, ami ellentmond annak,
hogy u kivétele el®tt teljesült az 1. tulajdonság.

5. Tétel. Szélességi keresés esetén (összefügg® irányítatlan gráf esetén)

1. A gráf élei nem ugorhatnak át szintet, vagyis, ha uv ∈ E , akkor |SZ(u) −


− SZ(v)| ≤ 1
2. Minden x csúcsra SZ(x) az 1 csúcsból az x-be vezet® legrövidebb út hossza.
3. Az egyik legrövidebb út : (1, . . . , p(p(x)), p(x), x)
4. Ha a gráf összefügg®, akkor minden csúcsot bejárunk az algoritmus során,
vagyis jó az algoritmus.

Bizonyítás : (El®z® tétel alapján)

*
v

14
1. Nem lehet uv típusú (lásd az ábrán) él a gráfban, mivel amikor u-t kivettük
a sorból v még nem lehetett benne a sorban, így u átvizsgálása során be
kellett kerülnie eggyel nagyobb szintszámmal.

2. A 3. állításban felírt (jobbról balra generált) csúcssorozat valóban egy út


lesz, és az 1. állítás miatt egy legrövidebb út is, mivel a gráf élei nem
tudnak szintet ugrani. A hossza nyilván SZ(x).
3. Jelölje d(1, x) egy x csúcs távolságát az 1-t®l. Indirekten tegyük fel, hogy
létezik olyan csúcs, ami 1-b®l elérhet®, és ahol nem jártunk. Legyen x∗ az

ilyenek közül egy olyan, amelyre d(1, x ) minimális.

d(1,x*)

x*

Jelölje az1-b®l x∗ -ba vezet® d(1, x∗ ) hosszú út utolsó el®tti csúcsát y ,



ekkor y közelebb van 1-hez, így x választása szerint y -ban jártunk, ezért

valamikor y szerepelt a sorban. Amikor megvizsgáltuk a szomszédait x -ot
is megtaláltuk volna.

Megjegyzés : ezúttal azt is újra beláttuk, hogy ez a bejárás egy feszít®erd®t


határoz meg.

2.4. Adott gráf kétszínezhet®-e?


Tudjuk (de most újra be is bizonyítjuk), hogy egy gráf akkor és csak akkor nem
kétszínezhet®, ha van benne páratlan kör. Színezzük a gráf csúcsait az alábbi
módon : Legyen piros, ha SZ értéke páros és kék, ha SZ páratlan.

Mikor nem jó ez a színezés ? Felhasználjuk, hogy a gráf élei nem ugorhatnak


át szintet. Probléma csak az egy szinten belül men® élekkel lehet. Legyen egy
ilyen szinten belüli él u és v között.

15
u v

Az algoritmust egészítsük ki az ELSE IF SZ(u) = SZ(v) THEN BAJ sorral.

BAJ : megkeressük u és v legközelebbi közös ®sét. Az így meghatározott kör


valóban páratlan, így a gráf nem kétszínezhet®.

A kör kiíratása : Felváltva a szül®re lépkedve keressük meg az x legközelebbi


közös ®st, és közben rakjuk v, p(v), . . . , p(. . . (p(v)) . . .), . . . x-et egy verembe
u, p(u), . . . , p(. . . (p(u)) . . .), . . . x-et pedig egy sorba. Ha kiürítjük a vermet, majd
a sort, akkor az adott kör csúcsait soroljuk fel, a sor utolsó elemét már nem írat-
juk ki.

2.5. Irányított gráfok


Minden csúcsot elérünk, amibe van 1-b®l x-be men® irányított út. Ekkor is
a legrövidebb ilyet találjuk meg. Ebben az esetben a gráf élei visszafelé már
átugorhatnak szintet, lefelé ugyanúgy nem.

Irányított gráfok esetén kétféle összefügg®séget is deniálhatunk :

 Gyenge összefügg®ség : Ha eltekintünk az irányítástól, akkor összefügg®-e a


gráf (azaz, ha például a gráfok élei közlekedési utakat jelölnek, bejárható-e
ez egy biciklin ?)




Ekkor lényegében az a kérdés, hogy G összefügg®-e.

 Er®s összefügg®ség : Bármely x, y ∈ V esetén létezik-e x-b®l y -ba men®


irányított út ?

Triviális megoldás : n darab szélességi keresés (minden csúcsból indítunk


egyet).


Jobb megoldás : Csinálunk egy szélességi keresést az eredeti gráfban (( G ,1)-
←−
en), majd még egy szélességi keresést a ( G ,1) gráfban. Így összességében
2 szélességi keresés elegend® az er®s összefügg®ség eldöntéséhez.

3. Kupac

Egy fa kupacrendezett, ha igazak rá a következ® tulajdonságok :

16
 Gyökeres fa, melynek csúcsaiban rekordok helyezkednek el.

 Minden rekordnak van egy kitüntetett mez®je, a K KULCS.

 Bármely v -re, ha létezik a p(v) szül®, akkor K(rek(p(v))) ≤ K(rek(v))


(ezt ezentúl K(v)-vel jelöljük), azaz v leszármazottaira igaz, hogy az ott
található kulcsok minimuma van v -ben. (Egy csúcsot mindig saját maga
leszármazottjának tekintünk !)

Kupac: (egyszer¶ kupac, bináris kupac)

 Kupac-rendezett bináris fa (egy csúcs gyermekeinek száma 0, 1 vagy 2)


 A levelek vagy egy, vagy két szomszédos szinten vannak, ez utóbbi esetben
igaz, hogy a fels® tele van, az alsón pedig balra vannak rendezve a levelek.
(Tehát a bináris fában a szinteket mindig balról jobbra haladva töltjük
fel.)

Tárolás : a kulcsokat az A tömbben tároljuk, ahol :

 A(1) a gyökér kulcsa

 Az i. csúcs bal ának kulcsa A(2i)-ben lesz, jobb áé pedig A(2i + 1)-ben.
 Az i. csúcs szül®jének kulcsa A(bi/2c).
 Ezenkívül még tárolunk egy VÉGE pointer (számot), mely megadja, hogy
pillanatnyilag hány elem van a kupacban.

Megjegyzés : Igazából 3 A-ban tároljuk a csúcsok


tömbre van szükségünk. Az
kulcsait, a B i-edik csúcsban tárolt objektum nevét, vagy a
tömbben vagy az
rekordra mutató pointert, és C(j) pedig a j nev¶ csúcs (ill. rekord) indexe az
A-ban és B -ben. Általában csak az A tömbbel való operációt adjuk meg, a többi
kitalálható. De példaként a FELBILLEGTET eljárásban megadjuk ezeket is.

Példa :

4 5

6 7 8 9

12 10 15

A: 2 4 5 6 7 8 9 12 10 15

17
M¶veletek :

Újkupac(A, n) : egy új, üres kupacot hoz létre (n az egy id®ben a kupacban
szerepl® elemek maximális száma).

Beszúr(A, új) : egy új elemet szúr be a kupacba.

Mintörlés(A) : a minimális kulcsú elemet kiveszi a kupacból és visszaadja.

Kulcs-csökk(A, elem, ∆) : a kupacban lév® elem kulcsát csökkenti ∆-val.


(Csak∆ ≥ 0 esetén kell m¶ködnie). Az elem-et úgy határozzuk meg, hogy rá-
mutatunk egy pointerrel (nem tudjuk megkeresni, mivel a kupacban nem tudunk
hatékonyan keresni), azaz a C(elem)-edik kupacelem kulcsát csökkentjük.

M¶veletek megvalósítása:

Újkupac(A, n) :
Újtömb(A, n) ; VÉGE:= 0

Beszúr(A, új) :
Ötlet : az adott, hogy hová kell felvenni egy új levelet, oda berakjuk. A kupac
tulajdonság csak egy él mentén romolhatott el. Ezen él mentén javítjuk a kupac-
tulajdonságot, ekkor felette egy másik él mentén romolhat el, így ismételjük,
amíg mindenhol helyre nem áll (lehet, hogy egészen a gyökérig el kell mennünk).

Beszúr(A, új) :
VÉGE++
A(VÉGE) :=K(új)
FELBILLEGTET(VÉGE)

FELBILLEGTET(i):
AA := A(i) ; BB := B(i)
WHILE i > 1 && A(bi/2c) > AA
A(i) := A(bi/2c)
B(i) := B(bi/2c)
C(B(i)) := i
i := bi/2c
A(i) := AA
B(i) := BB
C(BB) := i
Lépésszám : A fa mélysége blog nc, amib®l következik, hogy a FELBILLEG-
TET lépésszáma O(log n).

6. Tétel. Beszúrás után a kupactulajdonság helyreáll.

18
Bizonyítás : Úgy tekintjük, hogy mindig i és szül®je tartalmát felcseréljük
egy-egy lépésben. Állítás : mindig csak i és a szül®je közti él mentén lehet baj
a kupac-tulajdonsággal. Ez kezdetben igaz. Ha i gyökér, vagy szül®jének kulcsa
kisebb-egyenl®, akkor itt sincs baj, tehát kész vagyunk. Különben csere után
ezen él mentén a kupac-tulajdonság helyreáll, és mivel a szül®ben lév® kulcs
csökken, a másik gyereke felé men® él sem romlik el, tehát csak az új i és szül®je
közötti élen lehet baj.

Mintörlés(A) : Kicseréljük a gyökeret és az utolsó elemet, majd töröljük az


utolsó elemet. Itt két élen is elromolhatott a kupac-tulajdonság. Ha cserélni kell,
akkor a két gyerek közül mindig a kisebb kulcsúval cserélünk.

Mintörlés(A) :
CSERE(A(1), A(VÉGE)) ; VÉGE−−
LEBILLEGTET(1)
Return(A(VÉGE+1))

LEBILLEGTET(i):
AA := A(i) ; j := 2i + 1
WHILE j ≤ VÉGE
IF A(j − 1) < A(j) THEN j − −
IF A(j) < AA THEN A(i) := A(j); i := j; j := 2i + 1
ELSE j :=VÉGE+2
j−−
IF j ≤VÉGE && A(j) < AA THEN A(i) := A(j); i := j
A(i) := AA

7. Tétel. Mintörlés után a kupactulajdonság helyreáll.

Bizonyítás : Úgy tekintjük, hogy mindig i és kisebbik kulcsú gyereke tartal-


mát felcseréljük egy-egy lépésben. Állítás : mindig csak az aktuális i és a gyerekei
közti élek mentén lehet baj a kupac-tulajdonsággal. Ez kezdetben igaz. Ha i-
nek nincs gyereke, vagy mindkét gyerekének kulcsa nagyobb-egyenl®, akkor itt
sincs baj, tehát kész vagyunk. Különben csere után ezen élek mentén a kupac-
tulajdonság helyreáll, és csak az új i és gyerekei között romolhat el.

Lépésszám : O(log n)

Kulcs-csökk(K , elem, ∆) :
i := C(elem)
A(i) := A(i) − ∆
FELBILLEGTET(i)

Lépésszám : O(log n)

19
Alkalmazások:

Rendezés : üres kupacba sorban Beszúrjuk az elemeket, majd sorban Min-


törlünk, ez O(n · log n) lépés. Az alábbi, lineáris idej¶ Kupacépítés segítségével
kicsit jobb lesz. Ezt f®leg olyan estekben érdemes használni, amikor a feladat
például az els® k legkisebb elem kiíratása (nagyság szerinti sorrendben), ekkor
a futási id® O(n + k · log n) lesz.

KUPACÉPÍTÉS (nem alapm¶velet) n darab elem kupacba szervezése.

Tetsz®legesen feltöltjük a tömböt, majd alulról felfelé rakjuk rendbe a kupa-


cot.

Az algoritmus:
az adatok betöltése tetsz®legesen az A tömbbe.
FOR i = n/2 . . . 1(−1)
LEBILLEGTET(i)

Lépésszám : (összes lebillentések száma)=

n n n 1 2 3
2 ·0+
4 · 1 + 8 · 2 + . . . + 1 · dlog ne ≤ n · ( 4 + 8 + 16 + . . .)

a := ( 14 + 82 + 163
+ . . .), könnyen láthatóan ez a sor abszolút konvergens,
a 1 2 3 1 2 3
ezért az a szám létezik. Ekkor a −
2 = ( 4 + 8 + 16 + . . .) − ( 8 + 16 + 32 + . . .) =
1 1 1 1
= ( 4 + 8 + 16 + . . .) = 2 =⇒ a = 1
Azaz legfeljebb n darab lebillentés kell, így O(n) elemi lépés lesz a lépésszám.

Kitér® : Algoritmusok lépésszámát akarjuk megbecsülni. 1 Lépés-nek (ejtsd


nagy lépés) konstans sok elemi lépést fogunk nevezni a továbbiakban. S®t, ha

20
szükség lesz rá ezt a konstanst növelni is fogjuk az elemzés során (ez nem fog
gondot okozni, mert a nagyobb konstansba beleférnek a növelés el®tti kisebb
konstansok is).

Következmény : Ha n elemb®l a k darab legkisebbet keressük rendezve, akkor


ennek a lépésszáma O(n + k · log n) lesz.
További alkalmazások találhatók a következ® fejezetekben.

4. Minimális költség¶ feszít®fa és legrövidebb út

keresése

Adott : G = (V, E) összefügg®, irányítatlan gráf c : P


E → R élköltségekkel.
Feladat : Olyan feszít®fát keresünk, melyre c(T ) = e∈T c(e) minimális.

4.1. Kruskal algoritmusa


I. FÁZIS : rendezzük az éleket költség szerint : c(e1 ) ≤ c(e2 ) ≤ c(e3 ) ≤ . . . ≤
≤ c(ee ). Ez O(e · log n2 ) = O(e · log n) id®ben megy, ahol e = |E| és n = |V |.
II. FÁZIS : nyilvántartjuk a komponenseket : K = {{v1 }, {v2 }, . . . , {vn }}
T := ∅
FOR i = 1 . . . e
IF NOT ei két vége egy komponensben
THEN T := T + ei ; két komponens uniója.

Megjegyzés : persze ha azt is gyeljük, hogy T -ben mikor lesz n−1 él, akkor
már meg is állhatunk.

8. Tétel. Ez az algoritmus valóban minimális költség¶ feszít®fát ad.

Bizonyítás :

e1 e2 e3
T: + - +

+/- jeleket teszünk aszerint, hogy az adott él benne van-e T -ben (T az algorit-
mus által adott fa).

Legyen T ∗ olyan optimális fa, melyre a +/− sorozat a lehet® leghosszabban


megegyezik T -ével. Legyen az els® különbség az i. oszlopban.

21
1. eset (−/+) :

ei
T: -
T*: +

Ez nem lehetséges, mivel ha a T -be az algoritmus során nem vettük be az ei


élet, akkor ei két végpontja össze van kötve kisebb sorszámú élekkel, azonban :
j < i-re ej ∈ T ⇒ ej ∈ T ∗ . Ekkor ei T ∗ eddigi éleivel is kört alkot, ez pedig
ellentmondás.

2. eset (+/−) :

ei
T: +
T*: -

Mi lesz T ∗ + ei ? Mivel T ∗ feszít®fa, ebben lesz pontosan egy C kör. Legyen ej


a maximális index¶ éle a C -nek.

2. Állítás. j > i.

Bizonyítás : különben T -ben is ott lenne a C kör, hiszen T az i-nél kisebb


index¶ éleken megegyezik T ∗ -gal.

Következmény : c(ej ) ≥ c(ei )


T ∗ + ei − ej feszít®fa lesz (hiszen az egyetlen körb®l hagyunk el egy élt).
c(T ∗ + ei − ej ) ≤ c(T ∗ ) = OPTIMUM. Tehát T ∗ + ei − ej is optimális megoldás,
de ez ellentmondás, mert ez már az i. oszlopban is megegyezik T -vel.

Következmények :

1. Ha G-ben bármely két él költsége különbözik, akkor egyértelm¶ az opti-


mális feszít®fa.

2. Ha az összes lehetséges jó (költség szerint monoton növ®) élsorozatot vesszük,


majd ezekre lefuttatjuk az algoritmust, akkor megkapjuk az összes opti-
mális (minimális költség¶) feszít®fát.

22
Bizonyítás : T ∗ -hoz a sorrend megválasztása :

Az azonos költség¶eket úgy rakjuk sorba, hogy a T ∗ -ban szerepl® élek le-
gyenek el®l. Az el®z® bizonyítást végiggondolva könny¶ látni, hogy ekkor az
algoritmus éppen T ∗ -ot adja.

A II. FÁZIS megvalósítása : DISZJUNKT-UNIÓ és HOLVAN m¶veletekkel.


HOLVAN(x) := az x-et tartalmazó halmaz neve (ez egyértelm¶, mivel a hal-
mazok diszjunktak).

DISZJUNKT-UNIÓ (H1 , H2 ) : H := H1 ∪ H2 , azaz lecseréljük H1 -et és H2 -t


egy új, H nev¶ halmazra.

A II. FÁZIS során legfeljebb 2e darab HOLVAN és pontosan n − 1 darab


DISZJUNKT-UNIÓ m¶veletet hajtunk végre (mivel egy fát építünk).

4.2. DISZJUNKT-UNIÓHOLVAN adatstruktúrák


3 megvalósítást fogunk megvizsgálni :

A) megoldás : A tömbbel.

1 n
A H2 H2 H2 H2

INIT : FOR i := 1 . . . n
A(i) := i
HOLVAN(i) : Return(A(i))

Lépésszám : O(1).
DISZJUNKT-UNIÓ(H1 , H2 ) :
FOR i := 1 . . . n
IF A(i) = H2 THEN A(i) := H1
Lépésszám : O(n)
A II. FÁZIS összlépésszáma : O(e + n2 ) = O(n2 ).
Megjegyzés : S¶r¶ gráfokra jó megoldás.

B) megoldás : tömbökkel és láncolással okosabban :


4 tömböt használunk :
A: ebben tároljuk a halmazok neveit

23
L : a halmaz következ® elemére mutató pointereket tároljuk
K : K(i) az i. halmaz els® eleme
M : M (i) az i. halmaz mérete

3 6 8 20
A 1 1 1 1

L 6 8 20 0

következõ elem a halmazban nincs több elem a halmazban

INIT :
FOR i := 1 . . . n
A(i) := i; L(i) := 0; K(i) := i; M (i) := 1

HOLVAN(i) : Return(A(i))

DISZJUNKT-UNIÓ(H1 , H2 ) :
IF M (H1 ) < M (H2 ) THEN H1 ⇐⇒ H2
(felcseréljük a halmazok neveit =⇒ H1 lesz a nagyobb méret¶)
M(H1 ) := M(H1 )+M(H2 )
(a H2 végére f¶zöm a H1 -et, hogy ne kelljen végigmenni H1 -en.)
i := K(H2 )
WHILE L(i) 6= 0
A(i) := H1 ; i := L(i)
A(i) := H1
L(i) := K(H1 )
K(H1 ) := K(H2 )

Ez min(|H1 |, |H2 |) Lépés lesz. Tehát ennek a lépésszáma, ha egy konkrét


DISZJUNKT-UNIÓ m¶veletet végzünk, picit rosszabb is lehet (a konstans na-
gyobb), mint az el®z® megvalósításé.

Elemzés : most nem 1 darab DISZJUNKT-UNIÓ m¶velet lépésszámát vizs-


gáljuk, hanem azt, hogy az algoritmus az n−1 darab DISZJUNKT-UNIÓ m¶-
velet során összesen hány lépést tesz.

Az elemzés módszere a strigulázás lesz : táblázatokat készítünk el®re és az


algoritmus minden egyes Lépésénél húzunk egy strigulát a megfelel® táblázatba.
A végén összeszámoljuk, hogy összesen hány strigulát húztunk és ez lesz az
algoritmus Lépésszáma.

A Lépés konstansát úgy állítjuk be, hogy egy unió annyi Lépés legyen, mint
a kisebbik halmaz mérete.

24
Strigulázás :

1 i n
1. sor (lépés)
2. sor (lépés)
3. sor (lépés)

A j. m¶veletnél a j . sorban i alá húzunk egy strigulát, ha A(i) tartalma meg-


változott. Így pont min(|H1 |, |H2 |) strigulát húzunk.

9. Tétel. Minden i-re i alá legfeljebb log n strigulát húzunk.

Bizonyítás : Minden csúcs el®ször egy egy elem¶ halmazban volt, ezután min-
den strigula behúzásakor egy legalább kétszer akkora méret¶be kerül (hiszen a
kisebbik halmaz elemeinél húzunk strigulát). Így log n strigula után egy n ele-
m¶be kerül, tehát készen vagyunk.

Így n−1 darab DISZJUNKT-UNIÓ lépésszáma O(n · log n) lesz.

Ezért a II. FÁZIS lépésszáma O(e + n · log n).


Okok amiért II. FÁZIS gyorsításán érdemes dolgozni az I. FÁZIS-sal szem-
ben :

 Nem biztos, hogy végig kell menni az összes elemen, ha szerencsénk van,
a legkisebb n−1 élet elég ismerni, tehát a kupac segíthet.

 A rendezési algoritmus minden könyvtárban meg van írva gyorsan.

 El®fodul, hogy eleve rendezve kapjuk az éleket, így nincs is szükség az I.


FÁZIS-ra.
 Általában az élek költségei kis egész számok, így O(e) id®ben rendezhet®ek
(leszámláló rendezéssel).

 Sok más alkalmazásban is használják a HOLVAN és DISZJUNKT-UNIÓ


m¶veleteket.

C) megoldás : a DISZJUNKT-UNIÓ m¶veletet akarjuk 1 Lépésben meg-


valósítani, a HOLVAN m¶velet lesz lassabb.

Az ötlet: A halmazokat gyökeres fákként (mindenkinek csak szül®pointere


lesz) fogjuk ábrázolni, a gyökérben a halmaz neve lesz.

25
H1

HOLVAN(x) : x-b®l indulva mindig a szül®re lépünk, míg a gyökérbe nem


érünk, ott kiolvassuk a halmaz nevét.

DISZJUNKT-UNIÓ : r rang segítségével. A rang lényegében a fa magassága


lesz, pontosabban egy fels® becslés lesz a fa magasságára. Igazából a rangokat
nem csak a fákhoz (gyökerekhez), hanem minden egyes csúcshoz hozzárendel-
jük, de a fenti jelentésük csak gyökerek esetén lesz meg, a többi esetben az
algoritmushoz nem, csak az elemzéshez használjuk. A rangokat kezdetben csupa
nullára inicializáljuk. Mindig a kisebb rangú halmazt kötjük át a nagyobba :

H1

H2

Ha r(H1 ) < r(H2 )

H2
H1

26
Ha r(H1 ) > r(H2 )

H1
H2

Ebben a két esetben nem n® a rang.

Ha r(H1 ) = r(H2 )

H1 H2

r(H1 ) + + (A H1 rangja n®tt !)

Kitér® :
Egy csúcs (v ) mélysége : a gyökérb®l a v -be vezet® út hossza.

Egy csúcs (v ) magassága : ha l a v alatt a legtávolabbi levél, akkor a v -b®l


l-be vezet® út hossza.

A fa magassága:=gyökér magassága= maxv v mélysége=:a fa mélysége.

HOLVAN javítása : Ötlet : ha már sokat kell dolgoznunk, akkor dolgozha-


tunk egy kicsivel többet, ha ezzel a továbbiakban id®t spórolunk. (A többlet-
munka során kicsit helyrepofozzuk az adatstruktúrát.)

27
Módszerek:

Útrövidítés:

Még egyszer végigmegyünk a fában azx-t®l a gyökérig vezet® úton, és minden


szül® pointert a gyökérre állítunk (kivéve a gyökérét).

Útfelezés:

Az x-t®l a gyökérig vezet® úton minden csúcs szül® pointerét átállítjuk a


nagyapjára (kivéve a gyökérét és a közvetlenül alatta lév®ét). Ehhez nem kell
még egyszer végigmenni a fán, egyszer¶en egy lépéssel tovább megjegyezzük a
szül® pointereket. Ez a módszer akár jobb is lehet mint az útrövidítés, mivel itt
elég egyszer végigmenni a fában a keres®úton. Útrövidítés esetén pedig újabb
ciklust kell használnunk, amely megkezdése (el®készítése) id®t igényel.

Természetesen az útrövidítés és az útfelezés során semelyik csúcs rangja nem


változik.

28
Az útrövidítés és az útfelezés elemzése
A Kruskal algoritmus n−1 darab DISZJUNKT-UNIÓ és m = 2e darab
HOLVAN m¶veletet végez. Azt fogjuk elemezni, hogy összesen hány lépésben
valósíthatjuk ezt meg.

3. Állítás. r(v) az elején 0 és monoton n®. Ha v nem gyökér egy adott pilla-
natban, akkor sose lesz az, és az r(v) már soha többé nem változik.

4. Állítás. r(v) < r(p(v)) (ha v nem gyökér).

Bizonyítás :
DISZJUNKT-UNIÓ m¶velet : Ha a kisebb rangút kötöttük be a nagyobb alá,
akkor teljesül az állítás. Egyenl® rangúaknál szintén teljesül az állítás, mivel
ekkor az új gyökér rangját növeljük.

HOLVAN : Ha átkötöm, akkor nagyobb rangúba kötöm, és mivel átkötés el®tt


teljesült ez a tulajdonság, tehát nem romolhat el.

5. Állítás. x gyöker¶ fában van legalább 2r(x) darab elem, következésképpen


∀v : r(v) ≤ blog nc.

Bizonyítás :
1 pontú fára teljesül az állítás.
DISZJUNKT-UNIÓ : különböz® rangúak esetén nyilván teljesül. Egyenl® ran-
gúnál : mindkét fában legalább 2r(x)−1 darab elem van, a kötés után legalább
r(x)−1 r(x)
2·2 =2 lesz.

HOLVAN : nem változtatja meg ezt.

6. Állítás. Állítás: {v | r(v) = k} ≤


n
2k
.

Av := {w | w a v leszármazottja volt, amikor r(v) = k lett}

Belátjuk, hogy ha r(x) ≥ k és r(y) ≥ k és x 6= y ⇒ Ax ∩ Ay = ∅. (El®z®


állítás ⇒ |Ax | ≥ 2k , tehát ebb®l tényleg következik.)

Tfh. y lett kés®bb k rangú. Ekkor x ∈


/ y gyöker¶ fának a 4. állítás miatt.
Ugyanakkor Ax minden eleme x-szel azonos fában van. ⇒ valóban diszjunktak.

Deníció : Torony függvény : T (0) = 1 és T (n + 1) = 2T (n) .



Az inverze : G(n) = log (n) = min{k | T (k) ≥ n}
Megjegyzés: Ha n ≤ 265536 ⇒ G(n) ≤ 5 (Az univerzumban kevesebb, mint
65536
2 atom van.)

29
Rangcsoportok:

0 1 2 3 4 5 16 17 65536

R0 R1 R2 R3 R4

Ri := {T (i − 1) + 1 . . . T (i)}. v az Ri rangcsoportban van, ha r(v) ∈ Ri

Elemzés:
DISZJUNKT-UNIÓ : 1 Lépés

HOLVAN : annyi Lépés, ahány csúcsot érintünk az útfelezés/útrövidítés so-


rán.

Például :

5 Lépés

A HOLVAN m¶veleteket ügyesen fogjuk elemezni, minden egyes m¶velet


során annyi strigulát fogunk behúzni, ahány csúcsot érintettünk. Azonban a
strigulákat különböz® helyekre fogjuk behúzni. A végén összeszámoljuk (felülr®l
becsüljük) majd az összes helyen lév® strigulákat.

2 féle helyre fogunk strigulát húzni : vagy az adott m¶velethez :


1. HOLVAN
2. HOLVAN
.
.
.
m. HOLVAN

Vagy a csúcsokhoz (az alaphalmaz elemeihez) :


v1
v2
.
.
.
vn

30
Az i. HOLVAN m¶veletnél a strigulázási szabály :

A m¶velet során bejárt út bármely v csúcsára :

Az i. HOLVAN m¶velethez húzunk egy strigulát, ha :

 v vagy p(v) gyökér,

 vagy ha v szül®je nem ugyanabban a rangcsoportban (hanem nagyobban)


van, mint v.

Minden más esetben a v csúcshoz húzunk egy strigulát.

Az összes (m darab) HOLVAN m¶velet után megszámoljuk, hogy hány stri-


gula van a m¶veleteknél és mennyi a csúcsoknál.

Maximális rang ≤ log n ⇒ a legnagyobb rangcsoport (Rα ), ahol α ≤ G(log n) =


= G(n) − 1. Tehát legfeljebb G(n) darab rangcsoportunk van. (A 0-s csoporttal
együtt.)

Az i. HOLVAN sorában a strigulák száma legfeljebb 2+(G(n)−1) = G(n)+1,


mert az út mentén a rangcsoport legfeljebb G(n) − 1 -szer n®het.

Hány strigulát húzhattunk egy csúcs mellé :

 Csak akkor kezdünk el strigulákat húzni egy csúcs mellé, ha már nem
gyökér.

 Útfelezés/útrövidítés esetén a rang szigorú monotonitása miatt r(p0 (v)) >


0
> r(p(v)), azaz v új  p (v)-vel jelölt  szül®jének rangja szigorúan na-
gyobb, mint az átkötés el®tti szül®jéé.

Ha r(v) = r, akkor v a g := G(r)-edik rangcsoportban van.

10. Tétel. Ekkor a v csúcs mellé maximálisan T (g) − T (g − 1) strigula kerülhet


(g ≥ 1 esetén).

Bizonyítás : T (g) − T (g − 1) szám van a g . rangcsoportban. Ha már került


strigula v mellé, akkor r(v) nem változik, míg r(p(v)) id®ben n®. Azonban ha a g
rangcsoporton túln®tt, akkor az adott csúcs mellé már soha többé nem húzunk
több strigulát.

Strigulák összeszámolása:
N (g) : jelentse azt, hogy maximálisan hány csúcs lehet a g -edik rangcsoportban.
T (g)
X n n 1 1 n n
N (g) ≤ ≤ T (g−1)+1 (1 + + + . . .) = T (g−1) = .
2r 2 2 4 2 T (g)
r=T (g−1)+1

31
Összefoglalva: g rangcsoportbeli csúcsok mellé összesen legfeljebb T n(g) ·(T (g)−
− T (g − 1)) ≤ n strigula kerülhet.
Ez igaz g = 0-ra is, mivel legfeljebb n darab csúcs lehet a 0. rangcsoportban és
a 0-s rangcsoportú csúcsok mellé csak 1 strigula kerülhet.
Így összesen a csúcsok mellé legfeljebb G(n) · n strigula került.

Lépésszám: O(n − 1 + m · (G(n) + 1) + n · G(n)) = O((n + m) · G(n)), ahol


m a HOLVAN m¶veletek száma.

Ha a Kruskal algoritmus II. FÁZISÁT így valósítjuk meg összefügg® gráfra


(ekkor : m = 2e > n), akkor a lépésszám O(e · G(n)) lesz, ami gyakorlati szem-
pontból lényegében lineárisnak tekinthet®, de valójában a legrosszabb esetben
tényleg nem lineáris, nem csak mi voltunk nagyvonalúak az elemzésnél.

4.3. Prim algoritmusa


Az algoritmus ötlete: Kezdetben induljunk ki az 1-es számú csúcsból, majd
minden egyes lépésben b®vítsük ezt a fát (és az S csúcshalmazát), úgy, hogy a
bel®le kiinduló (S és V −S közötti) legkisebb költség¶ élet vesszük hozzá.

P M
S

min

T : aktuális fa
S : T-nek a csúcshalmaza
P := {v ∈ V − S | ∃uv ∈ E, u ∈ S}
M := V − S − P
Egy adott lépésnél az M -beli csúcsokkal nem foglalkozunk.
A P -beli csúcsokra két dolgot tartunk nyilván :

 K(v) := min{c(uv) | u ∈ S} - ha a v csúcsot kötöm be következ®nek,


akkor ez milyen költséggel fog járni.

32
 p(v) := argmin{c(uv) | u ∈ S} - ha ®t kötöm be, melyik csúcshoz kell
kötni (ha több ilyen csúcs is van, akkor közülük az egyik)

Az algoritmus:
P := {1}; S := ∅; M := V − {1}; T := ∅
INIT :
K(1) := 0; p(1) := 1
WHILE P 6= ∅
v legyen a P -ben minimális K(v) érték¶ csúcs
S←v←P
IF p(v) 6= v THEN T := T + {p(v), v}
FOR vu ∈ E
IF u ∈ P && c(vu) < K(u) THEN K(u) := c(vu); p(u) := v
IF u ∈ M THEN K(u) := c(vu); p(u) := v; P ← u ← M

Lépésszám:
Szomszédsági mátrix esetén : O(n2 ) (láttuk a szélességi keresésnél, hogy ennél
gyorsabban nem is lehet).

Éllistával, kupacokkal :

M¶velet Végrehajtások száma


Mintörlés n
Kulcs-csökkentés e
Beszúrás n

Mindegyik m¶velet O(log n) idej¶, ha kupaccal csináljuk, így az algoritmus


O(e · log n) idej¶ lesz.

Megjegyzés : ritka gráfok esetén ez sokkal jobb, mint az O(n2 ), de még javítani
fogunk rajta.

4.4. Dijkstra algoritmusa


Adott: G = (V, E); és c : E → R+ 0 (hosszúság függvény, néha költségnek hívjuk)
Feladat: keressük az s-b®l t-be vezet® legrövidebb utat.
Az algoritmus ötlete : Irányítatlan gráfokra jól szemléltethet®, ha elkészítjük
a gráfot : a csúcsok gyöngyök, az élek megfelel® hosszúságú cérnák. Lefektetjük
az asztalra, és s-nél fogva lassan felemeljük.

A megvalósítás nagyon hasonlít a Prim algoritmuséhoz. Minden egyes lépés-


ben azt fogjuk tárolni az egyes csúcsokra, hogy hol vannak, és hogy mi az eddig
ide talált legrövidebb út K(v) hossza (azaz ha v emelkedne fel legközelebb, ak-
kor milyen messze lenne s-t®l). Minden egyes lépésben a P -beli legkisebb kulcsú
csúcsot emeljük fel, majd szomszédain módosítjuk a kulcsot.

33
S: a leveg®ben lév® gyöngyszemek.

P: olyan gyöngyszemek, amelyek az asztalon vannak, de van olyan szom-


szédjuk, ami a leveg®ben van.

M: a többi csúcs.

Megjegyzés: Negatív élhosszakkal nem fog m¶ködni az algoritmus.

Példa :

2
-2

Az algoritmus:

P := {1}; S := ∅; M := V − {1}
INIT :
K(1) := 0; p(1) := 1
WHILE P 6= ∅
v legyen a P -ben minimális K(v) érték¶.
S←v←P
FOR vu ∈ E
IF u ∈ P && K(v) + c(vu) < K(u) THEN
K(u) := K(v) + c(vu); p(u) := v
IF u ∈ M THEN K(u) := K(v) + c(vu); p(u) := v; P ← u ← M

1. Deníció. Egy s −→ x út S -út, ha minden csúcsa S -ben van kivéve esetleg


x-et.

11. Tétel. Az algoritmus során minden WHILE végén:


(a) Bármely x ∈ S -re K(x) a legrövidebb s −→ x út hossza, és az ilyen
hosszúságú utak között van S -út is.
(b) Minden x ∈ P -re K(x) a legrövidebb S -út hossza.

Bizonyítás : (a) Elég az éppen S-be átrakott csúcsra belátni, legyen ez r. Az


r-be nem létezik K(r)-nél rövidebb út, mivel :

34
P M
S
r
h

>=0

Indirekt tegyük fel, hogy létezik ennél rövidebb Q út, ekkor az indukciós fel-
tevés szerint ez nemS -út, legyen az els® P -beli csúcsa w 6= r. Ekkor r deníciója
miatt K(r) ≤ K(w), és így az indukciós feltevés miatt a Q út s-t®l w -ig terjed®
része nem rövidebb, mint K(r) = r -be vezet® legrövidebb S -út hossza. Mivel
az élhosszak nemnegatívak, a Q út w -t®l r -ig terjed® része is nemnegatív, tehát
összesen Q hossza legalább K(r), ami ellentmond Q választásának.

Az állítások többi része hasonlóan bizonyítható.

12. Tétel. (a) Ha az S -be kerülés sorrendjében veszem a csúcsokat


(vi : i.-nek került S -be), akkor K(v1 ) ≤ K(v2 ) ≤ . . . ≤ K(vn ).
(b) Ha utoljára v került S -be, akkor minden u ∈ P -re K(v) ≤ K(u) ≤ K(v)+
+ maxxy∈E c(xy).

Bizonyítás szintén indukcióval, el®sz®r (b)-t érdemes. Ha utoljára v került


S -be, akkor minden u ∈ P -re K(v) ≤ K(u), mert a v -t úgy választottuk. A
kulcsmódosítások el®tt az indukciós feltevés (a) része miatt a második egyen-
l®tlenség is fennállt, és kulcsmódosítás közben nyilván nem romolhat el egyik
egyenl®tlenség sem. Ebb®l az (a) rész rögtön következik.

13. Tétel. Az x csúcsba vezet® egyik legrövidebb út (hátulról el®re állítjuk el®) :
s, . . . , p(p(x)), p(x), x.

Megjegyzések :

 Az algoritmus módosítható úgy, hogy pl. legszélesebb ill. legbiztonságo-


sabb utat keressen.

 Ha megengedjük a negatív élhosszakat, akkor a feladat N P -nehéz lesz.

35
 Irányított gráfok esetén, ha bármely irányított kör nemnegatív, akkor van
másik algoritmus (Bellman-Ford), amely helyes és polinomiális, de las-
sabb : O(e · n).
 Viszont aciklikus irányított gráfok esetén negatív élsúlyra is van kicsit
egyszer¶bb, O(e) idej¶ algoritmus, ezt használják pl. a PERT módszerben.

 Irányítatlan esetben, ha nincs negatív kör, akkor szintén létezik polinomi-


ális algoritmus, de az bonyolult.

Elemzés
Ugyanúgy, mint a Prim algoritmusnál, tehát szomszédsági mátrix esetén
O(n2 ) a lépésszám, éllista esetén, ha valamilyen kupacot használunk, akkor pe-
dig legfeljebb n darab Beszúrás, n darab Mintörlés, és e darab Kulcs-csök kell.

4.5. d-edfokú kupacok


2. Deníció. Egy fa d-edfokú kupac, ha

 minden csúcsnak legfeljebb d gyereke van


 kupacrendezett
 kiegyensúlyozott

Tömbös megvalósítás esetén 0 az els® index, és az i index¶ csúcs gyerekeinek


indexei d · i + 1, d · i + 2, . . . , d · i + d. Tehát a j szül®je b j−1
d c. A kiegyensúlyozás
itt (is) azt jelenti, hogy a tömbben nincs lyuk.

A m¶veleti id®k változása :

Beszúrás, Kulcs-csökkentés (felbillegtetés) : O(logd n)


Min-törlés (lebillegtetés) : d gyerekb®l minimum kiválasztása : d − 1+ az aktuális
kulccsal való hasonlítás = d. Mivel a fa mélysége logd n, így O(d · logd n) adódik.

Érdekes eset : d = 4. Itt :


log n
Felbill. : log n helyett log4 n = 2

Lebill. : 2 · log n helyett 4 · log4 n = 2 · log n


Tehát egyértelm¶en jobb.

A Dijkstra és Prim algoritmus lépésszáma d-edfokú kupaccal

36
Mi vajon a legjobb d? Erre általában nincs egyértelm¶ válasz, az egyes m¶-
veletek gyakoriságától függ.

Tekintsük a Dijkstra (vagy a Prim) algoritmust. Itt az egyes m¶veletek száma :

n db Beszúrás, Min-törlés

e db Kulcs-csökkentés

Az elv : gyorsítsuk amib®l sok van. A teljes lépésszám : O(n·d·logd n+e·logd n) =


= O(max(n·d·logd n, e·logd n)). Az els® tag d ≥ 3-ra szigorúan növ®, a második
szigorúan csökken®, tehát a maximum a lehet® legkisebb értéket ott veszi fel,
ahol a kett® egyenl®.

n · d · logd n = e · logd n
e
d =
n
lem
d∗ := max(2, )
n
-et érdemes használni, mert d-nek egésznek kell lennie és nem akarunk 1-et
alapnak.

Így a lépésszám O(e · logd∗ n). Ez ritka gráf esetén (ha e ≈ c · n) nem nagyon
segít, de s¶r¶nél igen :

Ha e ≥ n1+ ⇒ d∗ ≥ n ⇒ logd∗ n ≤ 1/ ⇒ a lépésszám : O( 1 · e) = O(e),


ha  konstans.

5. Amortizációs elemzés

5.1. Konvex burok keresése


Adott : pi = (xi , yi ) ∈ R2 (1 ≤ i ≤ n és xi 6= xj , ha i 6= j) pontok a síkban.
Feladat : A pontok konvex burkának megtalálása (a rajta lév® csúcsok felsorolása

37
pozitív irányú bejárás sorrendjében).

F tömb
(x2,y2)

(x1,y1) (x k+1,y k+1)

(x k,yk)

A tömb

Megoldás :
I. rendezzük a pontokat xi -k szerint (x1 < x2 < . . . < xn )
Id® : O(n · log n)
II. Egy adott ciklusban tudjuk az p1 , . . . , pi−1 pontok konvex burkát, külön
tároljuk balról jobbra rendezetten az F fels® és az A alsó ívet.
F OR i = 2 to n
Felez® kereséssel az alsó és a fels® íven végigmenve megkeressük az ug-
rópontot (az utolsó olyat, ami még az p1 , . . . , p i halmaz konvex burkán is rajta
van)
A és F további részét elfelejtjük, majd végükre rakjuk pi -t.
Megjegyzés : F (j) az ugrópont, ha az F (j − 1) és F (j) pontokat összeköt®
egyenes pi felett megy, de az F (j) és F (j + 1) pontokat összeköt® egyenes már
alatta.

Id® : 1 pont hozzáadása legfeljebb O(log n), így az összid® : O(n · log n).
Összefoglalva : síkban a konvex burok keresés O(n·log n) id®ben megoldható.
Itt is érdemes a második fázist gyorsítani (pl. lehet, hogy x szerint rendezve
kaptuk a pontokat).

14. Tétel. Ha a felez® keresés helyett jobbról indulunk és sorban megvizsgáljuk,


hogy az adott pont-e az ugrópont, akkor a második fázis ideje csak O(n).

5.2. Az amortizációs elemzés


Bevezetjük az alábbi jelöléseket :
T Ii : az i. m¶velet tényleges ideje.
Potenciál : bármely állapothoz hozzárendelünk egy nemnegatív valós számot.
P0 = 0 és ∀Pi ≥ 0.
AIi : az i. m¶velet amortizációs ideje.
AIi := T Ii + ∆P = T Ii + (Pi − Pi−1 ).

38
P P P
Így i AIi = i T Ii + PT − P0 ≥ i T Ii .
Tehát az amortizációs id® egy fels® becslést fog adni a m¶veletek összidejére.

Megjegyzés : Tipikusan olyan állításokat bizonyítunk, hogy pl. egy-egy m¶-


velet amortizációs ideje legfeljebb O(log n), ebb®l következik hogy m m¶velet
elvégzésének tényleges összideje legfeljebb O(m · log n). De persze 1 adott m¶ve-
let elvégzésének tényleges ideje lehet akár c · n is, ezért az amortizációs elemzés
nem hasznos valós idej¶ folyamatok elemzésekor (pld. : egy repül®gép navigációs
rendszerét®l nem azt várjuk, hogy átlagosan gyorsan döntsön, hanem azt, hogy
minden egyes vészhelyzetre azonnal reagáljon).

5.3. Példa : a konvex burok keresés amortizációs elemzése


A 14 tétel bizonyítása :

A potenciál deniálása : P := |F | + |A|.


1 Lépés deniálása : F-ben i∗ megkeresése l+1 Lépés legyen, ha F végér®l
l csúcsot töröltünk ki.
(Ugyanígy deniáljuk a Lépéseket A esetén is, ekkor l0 a törölt csúcsok száma).

Amortizációs elemzés : AIk+1 = T Ik+1 + ∆P = ((l + 1) + (l0 + 1) + 1 + 1) −


− ((l − 1) + (l0 − 1)) = 6
Azaz egy csúcs beszúrásának amortizációs ideje 6. Ez azt jelenti, hogy 6n
Lépésben, azaz O(n) id®ben megoldható a feladat. Az elemzés persze attól m¶-
ködik, hogy ha egy csúcsot F -b®l vagy A-ból kidobunk, akkor azzal kés®bb már
sose kell foglalkoznunk, hiszen már az aktuális ponthalmaz konvex burkának is
bels® pontjai. Összesen persze a két tömbb®l kevesebb mint 2n esetben dobha-
tunk ki pontot.

6. Fejlettebb kupacok

6.1. Fibonacci kupac


Tulajdonságai :

 Több kupacrendezett fából áll, amelyekben egy csúcs gyerekeinek száma


nincs korlátozva, és nem is kiegyensúlyozottak.

 a gyökerek ciklikusan két irányban körbe vannak láncolva,

 a kupacot egy H pointerrel azonosítjuk, amely a minimális kulcsú gyökérre


mutat.

Tárolás:

39
 Minden elemhez 4 pointert tárolunk : szül®je, bal testvér, jobb testvér, egy
gyereke (mindegy, hogy melyik gyerek : ®k is ciklikusan két irányban körbe
vannak láncolva, így gyorsan megtalálhatjuk ®ket).

 r(v) ∈ N : a v elem rangja (a gyerekeinek a száma).

 m(v) ∈ {0,1} : jelöl® bit.

M¶veletek :

Beszúrás(új) :

 a gyökerek közé új gyökérként (1 pontú fa) felvesszük új-at.

 Ha KU LCS(új) < K(H), akkor a H -t az új-ra állítom.

új elem

Id® : O(1).

Mintörlés :

 M IN := H , a végén Return(M IN )
 I. fázis : H gyermekeit gyökeresítjük és a szül® pointereiket nil-re állítjuk.

 II. fázis : Amíg létezik 2 azonos rangú gyökér, addig LIN K m¶velettel
összekapcsoljuk ezeket.

40
 III. fázis : végigmegyünk a gyökereken és a minimális kulcsú gyökérre ál-
lítjuk H -t.

Megjegyzés : a végén tudni fogjuk, hogy minden elem rangja legfeljebb R=


= O(log n).
I. fázis ideje:
a gyerekek bef¶zése a gyökerek listájába konstans id®ben megy, a szül® pointerek
nil-re állítása R = O(log n) lépésben (H -nak legfeljebb R gyereke lehet).

LIN K(u, v) m¶velet :


Ha K(u) ≤ K(v), akkor v -t kötöm u alá és u rangját növelem eggyel, különben
u-t v alá (ebben az esetben v rangja n® eggyel). Visszaadja annak a csúcsnak a
nevét (pontosabban egy pointert arra a csúcsra), amely gyökér maradt.

1 LIN K Ideje : O(1). Ezt fogjuk el®ször 1 Lépés-nek tekinteni.

II. fázis megvalósítása :


T segédtömb segítségével : T (0 . . . R).
T indexe : rangok
T (i) értéke : mutató egy i rangú gyökérre, ha van az eddig vizsgáltak között, nil
különben.

Az algoritmus:
FOR i = 0...R
T (i) := nil
FOR v ∈ gyökerek
w := v
WHILE T (r(w)) 6= nil
w := LIN K(w, T (r(w)))
T (r(w) − 1) := nil
T (r(w)) := w
Id® : Ha a Lépésben a konstanst 2-szeresére vesszük, akkor
T I = O(log n) + #LIN K.
A Fázis végén legfeljebb R+1 darab gyökér marad. (Minden lehetséges
ranghoz legfeljebb 1.)

A LIN K -elések után az alábbi alakú ún. kanonikus fák (vagy binomiális
kupacok) keletkeznek (mindaddig, míg csak Beszúrás és Mintörlés m¶veleteket
végeztünk)

41
1. rangú 2. rangú 3. rangú

III. fázis ideje:


Legfeljebb R = O(log n) összehasonlítással megy.

Összefoglalva: T I = O(log n) + #LIN K

1. potenciál: gyökerek száma


Beszúrás : AI = O(1) + 1 = O(1).
Mintörlés : AI ≤ (O(log n) + #LIN K) + (−1 + R − #LIN K) = O(log n).

Kulcs-csökk :(H , v , ∆) :
Levágjuk v -t a szül®jér®l és gyökeresítjük (bef¶zzük a gyökerek listájába),
majd :

 K(v) :=K(v) - ∆
 IF K(v) < K(H) THEN H := v

Ennek a lépésnek a tényleges ideje 1 Lépés, az amortizációs ideje 2 Lépés.

Látszólag készen vagyunk, de van egy nagy baj : Így nem marad igaz, hogy
a rang legfeljebb R = O(log n).
Szeretnénk : ha r(v) = r, akkor v -nek van legalább cr (c > 1) leszármazottja
(ami persze max. n lehet). Ez Kulcs-csökk nélkül még teljesülne, de így nem
teljesül : ha minden unokára csinálnak egy-egy Kulcs-csökk m¶veletet, akkor
v -nek saját magán kívül csak a gyerekei maradnak meg leszármazottnak, míg
rangja nem változik :

42
Megoldás : m(v) jelz®bit használata :

 Ha gyökér : 0 (gyökeresítéskor visszaállítjuk 0-ra, ha nem az volt)

 Ha v nem gyökér és levágjuk egy gyerekét, akkor m(v) := 1.

Kaszkád-vágás : addig vágunk, amíg a szül®jének jelz®bitje nem 0. (Utána


ezt a 0-t 1-re állítjuk, ha nem gyökéren van.)

0 0
v
1 1
1
1 1

Tehát a Kulcs-csökkentés végleges megvalósítása :

Kulcs-csökk :(H , v , ∆) :
Levágjuk v -t a szül®jér®l és gyökeresítjük (bef¶zzük a gyökerek listájába),
majd :

 A Kaszkád-vágást folytatjuk, addig, amíg az utoljára gyökeresített csúcs


szül®je jelöletlen nem lesz, és a végén ezt a szül®t megjelöljük, ha nem
gyökér

 K(v) :=K(v) - ∆
 IF K(v) < K(H) THEN H := v

Az elemzéshez egy új potenciált kell deniálni :

2. (Végs®) Potenciál : #gyökerek +2 · #jelölt csúcsok.


A Beszúrásnál és Mintörlésnél nem kell kaszkádvágás ⇒ itt a potenciálválto-
zás csak kevesebb lehet, mint az 1. Potenciállal ( H gyerekeinek gyökeresítésekor
néhány jelz®bit 0-ra változhat) ⇒ ezek AI -je jó marad !

Kulcs-csökk : k db vágás van a kaszkádvágás során, ekkor legalább k−1


helyen 0-ra változott meg a jelöl®bit, míg 1-re csak legfeljebb 1 helyen.

T I : k Lépés
∆P ≤ −2(k − 1) + k + 2 = 4 − k .

 −2(k − 1) : jelöl®bitek száma legalább ennyivel csökken

43
 k: új gyökerek

 2: ha nem gyökérben hagytuk abba a vagdosást, lesz egy új jelölt

AI = TI + ∆P ≤ 4.

Most belátjuk, hogy a rang nem lehet túl nagy :

y1 y2 yr(x)

Egy tetsz®leges id®pontban indexeljük az x csúcs y1 , . . . , yr(x) gyerekeit a gye-


rekké válás sorrendjében (azaz, hogy mikor lett utoljára x gyereke).

15. Tétel. r(yi ) ≥ i − 2

Bizonyítás :

yi

y1 yi-1

Amikor yi az x gyereke lett (utoljára) :

LIN K → rangjuk egyenl® volt → ekkor a rangjuk legalább i − 1 volt →


azótayi rangja csak egyet csökkenhetett, különben levágtuk volna x-r®l.

3. Deníció. Sk jelölje azt, hogy egy legalább k -adrangú csúcsnak minimum


hány leszármazottja lehet (bármikor).

Megjegyzés : minden elem leszármazottja saját magának.

S0 = 1
S1 = 2

k−2
16. Tétel.
X
Sk ≥ Si + 2
i=0

Bizonyítás :

44
x

y1 y2 yi yk

S0 Si-2 Sk-2

Fibonacci számok : F0 = 0 ; F1 = 1 ; Fn+2 = Fn+1 + Fn

17. Tétel. Sk ≥ Fk+2 .

Bizonyítás : Indukcióval triviális.

√ !k √ !k
5+1 5+1
Fk+2 ≥ , azaz : Sk ≥
2 2
Emlékeztet® n jelöli a kupacban szerepl® elemek maximális számát. Tehát
bármely v elemnek legfeljebb n leszármazottja lehet ⇒ r(v) ≤ log“ √5+1 ” n ≈
2

≈ 1,44 · log n. Ez lesz R értéke.

Azaz beláttuk, hogy a Fibonacci-kupaccal :

n db beszúrás
n db mintörlés
e db kulcs-csökkentés
összes ideje : O(n log n + e).

Ebb®l következik :

18. Tétel. A Dijkstra és a Prim algoritmus futási ideje Fibonacci-kupaccal


O(n log n + e).

Megjegyzések :

 A Dijkstra-ban ez alá nem lehet lemenni (mivel tud rendezni és összefüg-


g®séget is el tud dönteni).

 A Prim-nek is ez a legjobb futási ideje, de minimális költség¶ feszít®fát


m < n log n esetén lehet gyorsabban is keresni.

6.1.1. Amortizációs id® és a strigulázás kapcsolata


Amikor egy m¶veletet végzünk, akkor kapunk a m¶velet megengedett amorti-
zációs idejének megfelel® $-t. Ha az algoritmust kevesebb Lépésb®l tudjuk vég-
rehajtani, akkor a maradékot berakjuk a bankba. Ha viszont több Lépésre van
szükségünk, akkor a különbözetet ki kell vennünk a bankból.

45
FIZETÉS BANK

IIIIIIIII TI<AI IIIII AI-TI

IIIIIIII IIII TI'>AI' -(TI'-AI')


AI' TI'-AI'

Kell : A bank pénze ne mehessen le negatívba. Ezt pont azzal biztosítjuk, hogy
megköveteljük, hogy P ≥ 0 legyen, ugyanis egy adott pillanatban a bankban
lev® pénz éppen az aktuális potenciál.

6.2. Párosítós kupacok


Ami nem annyira jó a Fibonacci-kupacban :

 nagy helyigény (sok pointer)

 nagy szorzók vannak (1,44) és az 1 Lépés is sok kis elemi m¶veletb®l áll.

Párosítós kupac a gyakorlatban: mindig jobb, mint a Fibonacci, de még nem


találtak hozzá olyan potenciált, amivel be tudnák látni ugyanazokat az amorti-
zációs id®ket.

Ötlet : A Fibonacci-kupacot akarjuk utánozni, de bináris fával és kevesebb


pointerrel. El®ször emeljük fel a H gyökeret és helyére f¶zzük be a gyerekeit.
Majd forgassuk el 45◦ -kal az egészet, ez lesz a párosítós kupac. Tehát itt a bal
gyerek felel meg az els® gyereknek, a jobb gyerek a következ® testvérnek.

v
v
a

a b c b
c

A párosítós kupac deníciója :

 Bináris fa.

 Az egyetlen gyökérnek (ami H -nak felel meg) nem lehet jobb gyereke.

 Félig kupacrendezett : K(v) ≤ K(x) a v bal ának minden x leszárma-


zottjára.

Így csúcsonként akár 2 pointert is nyerhetünk, ha használjuk még a bináris


fák memóriatakarékos megvalósítását : csúcsonként 2 pointer és 1 bit.

46
1 bit : bal vagy jobb gyerek.

47
1. pointer :

 bal gyerek, ha létezik

 jobb gyerek, ha csak az létezik.

 nil : ha egyáltalán nincs gyereke.

2. pointer :

 ha ® maga bal gyerek, akkor a testvérre mutat, ha létezik, különben a


szül®re.

 Ha jobb gyerek : a szül®re mutat.

 Gyökérben nil.

Könny¶ Hf : egy eredeti pointer szerinti lépéshez így két lépés és egy harma-
dik kiolvasás-vizsgálat kell.

Vissza a párosítós kupachoz : deniálnunk kell még a m¶veleteket.

LIN K m¶velet :

y
x 4 y 3 x
a b LINK a b

A B A B

Id® : O(1).

Kupacm¶veletek :

Beszúrás :1 elem¶ új kupac + LINK

Kulcs-csökk :

 kivágjuk az elemet

 csökkentjük a kulcsot

 majd LINK.

48
v

T I = O(1).
Mintörlés :

vágás
8
1

7
2

6
3

5
v
4

A gyökeret levágjuk, majd az új gyökért®l jobbra lefelé haladva minden élet


elvágunk. Így sok kisebb párosítós kupacunk keletkezik. Össze kell LINK-elni a
szétvágott kupacokat.

A legjobb megoldás : Elindulunk lefelé és párosával összeLINKeljük a ku-


pacokat, majd ezután alulról felfelé haladva végzünk LINK-eléseket (mindig a
következ®t az el®z® eredményéhez).

Miért ez a legjobb : Az a rossz, ha a két linkelend® kupacnak nagyon eltér a


mérete. Az a jó, ha kb. egyenl® nagyságúakat linkelünk, ezt próbáljuk elérni.
P
Ami bizonyítható : a P = v log(#v leszármazottai) potenciállal O(log n)
az amortizációs ideje minden m¶veletnek.

Azonban a gyakorlati futtatások azt mutatják, hogy igazából a Kulcs-csökkentések


átlagos ideje konstans.

49
6.2.1. A párosítós kupacok legjobb változata
Ötlet : Még lustábban. Nem jó kicsiket nagyobbakkal LINK-elni. Ezt tipikusan
a Beszúrás és Kulcs-csökk m¶veleteknél tesszük. Ezért ennél a két m¶veletnél
LINK-elés helyett egy külön segédterületre rakjuk ®ket.

segédterület

Beszúrás Beszúrás . . .

A segédterületet tárolhatjuk a gyökér jobb leszármazottaiként. Ebben az esetben


csak a Mintörlésnél kell többet dolgoznunk.

Mintörlés :

 El®ször a segédterületen végzünk LINK m¶veleteket. Párosával keletke-


zési sorrendben LINK-elünk, majd újra elölr®l párosával, míg az egész
segédterület 1 kupac nem lesz.

 Összelinkeljük az eredeti és a segédkupacot.

 Majd hagyományos Mintörlés.

6.3. r-kupacok
Dijkstra algoritmushoz : Feltesszük, hogy az élek hossza a [0, C] tartománybeli
egészek ⇒ a kulcsok a [0, (n − 1)C] tartományban lesznek.

Monoton kupac tulajdonságai :

 Mintörlések kulcsai az id® el®rehaladtával monoton n®nek

 Ha az utolsó Mintörléskor a kulcs dmin , akkor az összes aktuális kulcs


eleme a [dmin , dmin + C] tartománynak.

Megjegyzés : a Dijkstránál használt kupac ezeket teljesíti.

Monoton kupacokat, ha C nem túl nagy, az úgynevezett r-kupacokkal


(r = radix ill. redistributable) érdemes megvalósítani.

Legyen : B = dlog(C + 1)e + 2

50
u1 u2 u3 u4 u5

B1 B2 B3 B4 B5

Vödrök :

B1 , B 2 , . . . , BB vödröket készítünk. A Bi vödörhöz az ui fels® korlátot és


a range(Bi ) számintervallumot rendeljük. Egy Bi vödörbe azokat az elemeket
rakjuk, amelyek kulcsa eleme a range(Bi )-nek.

range(Bi ) = [ui−1 + 1, ui ].

 minden id®ben : |range(Bi )| ≤ 2i−2 , ha i = 2, . . . , B − 1 és |range(B1 )| =


= 1, |range(BB )| = nC + 1.

Kezdetben :
u0 = -1
ui = 2i−1 − 1
uB = nC (ebbe a vödörbe biztosan belefér minden elem)

range(B1 ) = [0]
range(B2 ) = [1]
range(B3 ) = [2, 3]
range(B4 ) = [4, 5, 6, 7]
.
.
.

Cél : A kupac elemeit úgy tárolni, hogy v a Bi -ben legyen, ha K(v) ∈


range(Bi ). Egy vödör tartalma egy oda-vissza láncolt lista lesz.

M¶veletek :
Beszúrás :
FOR j := B . . . 1(−1)
IF uj−1 < K(v) THEN v → Bj és kilépünk a ciklusból.

Megjegyzés : 1 elem beszúrására legfeljebb T I = B.


Kulcs-csökk :

 K(v) := K(v) − ∆
 IF másik vödörbe THEN kivesszük, elindulunk balra és megkeressük a jó
vödröt.

Mintörlés :

 Ha B1 6= ∅ → tetsz®leges elemét töröljük és visszaadjuk.

51
 Különben legyen Bj az els® nem üres vödör. Végignézzük a Bj vödör
minden elemét. Legyen v a minimális kulcsú, dmin := K(v).
 Átcímkézzük ui -ket :
u0 := dmin − 1
u1 := dmin
ui := min(ui−1 + 2i−2 , uj ), ha i = 2, . . . , j − 1.
 Ezután Bj minden elemét egyesével balra mozgatva berakjuk a neki meg-
felel® vödörbe, kivéve v -t, amelyet törlünk.

19. Tétel. Ezután Bj minden elemének a helye valamilyen i < j -re Bi lesz.

Bizonyítás : Miveldmin ∈range(Bj ), ezért dmin ≥ uj − 2j−2 + 1. Emiatt az


j−3
átcímkézések után uj−1 = min(uj , dmin + 1 + 2 + . . . + 2 ) = min(uj , dmin +
j−2
+2 − 1) ≥ uj , tehát nem lesz kisebb, mint uj átcímkézés el®tti értéke, így
minden elemet tényleg balra kell mozgatni. Mivel a Bj el®tti vödrök üresek
voltak, és Bj -ben semelyik elem kulcsa nem kisebb, mint dmin , mindenkit jól el
tudunk helyezni.

Amortizációs elemzés
X
A potenciál : P = b(v), ahol b(v) a v -t tartalmazó vödör sorszáma.
v∈kupac
Egy Lépés legyen (el®ször) 1 elem eggyel balra átrakásának ideje (kivesszük,
ellen®rizzük, hogy oda való-e, berakjuk).

M¶veletek:
Beszúrás(v) : T I = (B + 1 − b(v)) + 1
(B + 1 − b(v)) : megkeressük, hogy hová kell beszúrni az elemet.
1 : beszúrjuk.
∆P = b(v)
Így AI = B + 2.

Kulcs-csökk : T I = 1 + (b(v) − buj (v))


∆P = buj (v) − b(v).
Tehát AI = 1.
Min-törlés :
Ha B1 nem volt üres, akkor nyilván AI = 1. Egyébként :
 X 
T I = j + |Bj | + j + 1 + j − buj (u) , ahol
u∈Bj ,u6=v
j : Bj , az els® nem üres vödör megkeresése
|Bj | = a j . vödörben lev® elemek száma : végignézzük vj tartalmát és
kiválasztjuk v -t,

52
j : ui -k átcímkézése,
utolsó tag : balra pakolások ideje (1+ a v kihagyása).

Megjegyzés
X :

(j − buj (u)) ≥ |Bj | − 1, mivel (j − buj (u)) ≥ 1 a 19. tétel miatt.


u∈Bj ,u6=v

Egy Új Lépés := 2 · Lépés (tehát kétszer annyi elemi lépés), így

X  X 
T I ≤ j +|Bj |/2+1/2+(1/2)· j −buj (u) ≤ j +1+ j −buj (u)
u∈Bj ,u6=v u∈Bj ,u6=v

X 
Mivel ∆P = −j − j − buj (u) , ezért AI ≤ 1.
u∈Bj ,u6=v

Tehát ha például n Beszúrást, m Kulcs-csökkentést, és n0 ≤ n Mintörlést


AI ≤ n · (B + 2) + m + n0 ≤ n · (log C + 5) + m
P P
végzünk, akkor : TI ≤
darab Lépés, így O(m + n · log C) összes futásid®t kapunk (ha C ≥ 2). Tehát a
Dijkstra futási ideje [0, C]-beli egész hosszak esetén r-kupaccal : O(e + n · log C).

Ennél a kupacnál is jobbat készített Thorup, legalábbis elméletileg (gyakor-


lati megvalósításról és tesztekr®l nem tudunk) :

Feltételek : w= szóhossz és w ≥ log n, w ≥ log C , ahol C a hosszak maxi-


muma (a hosszak itt is nemnegatív egészek). Tehát azt tesszük fel, hogy minden
élhossz és egy csúcs címe belefér 1 szóba. Valamint feltesszük, hogy 1 Lépésben
2 szó összege és szorzata kiszámolható.

Ezen feltevések mellett Thorup kupaca a legrosszabb id®ben számolva a Be-


szúrást és Kulcs-csökkentést O(1) id®ben, a Mintörlést pedig O(log log min(n, C))
id®ben elvégzi.

Ezzel a kupaccal a Dijkstra O(e + n · log log min(n, C)) idej¶ lesz.

7. Szótárak

Legyen adott egy U univerzum és egy ezen a halmazon értelmezett < rendezés.

Feladat : adatstruktúra készítése S⊂U elemek tárolására. S (a pillanatnyi


szótár) minden elemét csak egyszer tároljuk.

M¶veletek :
Keresés(x) :

 Könnyített : x ∈?S

53
 Nehezebb : keressük meg x-et ha x ∈ S, különben pedig N IN CS .

Ha egy szótár csak ezt az egy m¶veletet tudja megvalósítani, akkor statikus
szótárnak nevezzük.

Ha a szótár nem statikus, akkor :

Beszúrás(x) :

 Könnyített : garantált, hogy x∈


/ S, és beszúrjuk. (S := S + x)
 Nehezebb : ha x∈
/ S, akkor S := S + x.

Ha egy adatstruktúrában mindkét m¶velet megoldott, akkor szótárról be-


szélünk.

Törlés(x) : ha x ∈ S, akkor S := S − x.
Ha mindhárom m¶velet megoldott, akkor az adatstruktúra szótár törléssel.

Ezen m¶veleteken kívül az alábbi m¶veletek is hasznos kiegészít®k lehetnek :

 min(S)
 max(S)
 Tólig(S,a,b) : = {x ∈ S | a ≤ x ≤ b}
 Fésül(S1 ,S2 )
 Olvaszt(S1 ,a,S2 ) : Tudjuk, hogy S1 < a < S 2 és S := S1 + a + S2 .
 Szétvág(S,a) : Szétvágja S -et olyan S1 és S2 -re, hogy S1 < a < S 2 .

Szótárak megvalósítása : Ha |U | kicsi : tárolhatjuk karakterisztikus vektorban


pointerekkel az elemeket. Az indexek U elemei lesznek.
Innent®l feltesszük, hogy |U | nagy.

Statikus szótárak esetén : S elemeit rendezett tömbben tároljuk és felez®


keresést használunk.

x<?T[n/2]
i h

x<?T[n/4]

i h
. . .
. . . . . .

54
7.1. Bináris keres®fa
Ez az el®z®vel lényegében ekvivalens.

1. Küls® tárolású : a fa leveleiben vannak az adatok, a többi csúcsban pedig


U elemei szerepelnek Útjelz®kként.

u1
i h

u2 u3

i h

s
. . .

Keresés(x) :
Legyen r a gyökér.
REPEAT
IF x ≤ u(r) és ∃ bal(r) THEN r := bal(r)
IF x > u(r) és ∃ jobb(r) THEN r := jobb(r)
IF r levél THEN
IF x = u(r) THEN RETURN(r)
ELSE RETURN(x ∈
/ S)
ELSE RETURN(x ∈
/ S).
Megjegyzés : A levelekben balról jobbra haladva S elemei rendezve vannak.

2. Bels® tárolású : a fa csúcsai megfeleltethet®k S elemeinek. A keresés so-


rán 3-felé elágazó IF van, amit direktben elég kevés nyelv támogat (pl.
FORTRAN). Viszont kevesebb tárhelyet foglal. Az áttekinthet®ség miatt
a fa minden egy-gyerekes csúcsa alá odaképzelünk egy, minden levele alatt
kett® ktív levelet. Tehát minden valódi csúcsnak pontosan két gyereke
van.

s'

Keresés(x) : Legyen r a gyökér.


REPEAT
IF r ktív levél THEN RETURN(x ∈
/ S)
IF x = u(r) THEN RETURN(r)
IF x < u(r) THEN r := bal(r)
IF x > u(r) THEN r := jobb(r)

55
A bels®-és küls® tárolású fák összehasonlítása :

Küls® Bels®
Memória ∼ 2n kulcs n kulcs
Mélység dlog ne dlog(n + 1)e − 1
Keresés dlog ne összehasonlítás dlog ne − 1 összehasonlítás, de 3 irányú !

Megjegyzés :

 A három irányú összehasonlítás a legtöbb környezetben 2 lépés lesz !

 Az esetek túlnyomó részében bels® tárolású keres®fát fogunk használni.

 Bels® tárolású fánál sikertelen keresés esetén visszaadjuk a megtalált ktív


levelet is (azaz a szül®jére egy pointert és egy biten, hogy bal vagy jobb
gyereke-e). Ez a beszúrásnál fog segíteni.

7.1.1. Optimális bináris keres®fa


Statikus szótárat akarunk tárolni úgy, hogy feltételezzük, hogy rengetegszer kell
majd benne keresni. Ezen keresések összidejét akarjuk minimalizálni.

Adottak :

 S = {a1 < a2 < . . . < an } a szótár elemei.

 ai -t pi valószín¶séggel keressük.

 ha ai < b < ai+1 , az ilyen b-ket qi valószín¶séggel keressük.

• ha b < a1 , akkor b-t q0 , ha pedig an < b, akkor b-t qn valószín¶séggel


keressük.

Egy adott T keres®fa esetén egy keresés várható lépésszáma, melyet a fa


költségének is hívunk :
n
X n
X
E(keresés)=c(T )= pi (d(ai ) + 1) + qi d(i)
i=1 i=0

 d(ai ) : az ai -t tartalmazó csúcs mélysége ; a +1 azért kell, mert a gyökér


keresése sem 0 lépés.

 d(i) : az i. (balról jobbra számozva) ktív levél mélysége.

56
Feladat : Minimális költség¶ bináris keres®fa konstrukciója, azaz olyan T , amely-
re c(T ) minimális.

Megoldás : Ha ak lenne az optimális T fa gyökerében :

ak

T1 T2

a 1 ... a k-1 ak+1 ... an

Ha ez az optimum, akkor szükségképpen T1 és T2 is optimális fa (a bennük


szerepl® ai -k által meghatározott részfeladatra). Ez a szuboptimalitás elve.

Megjegyzés : Azok a feladatok, amelyekre a szuboptimalitás elve teljesül,


többnyire megoldhatóak hatékonyan.

Ti,j := ai+1 . . . aj szavakon. Ennek gyökere ri,j , költsége ci,j ,


optimális fa az
súlya pedig wi,j = qi + pi+1 + qi+1 + . . . + pj + qj (wi,j az a valószín¶ség, hogy
belépünk egy Ti,j fába).

Inicializálás :
Ti,i = ∅ ; ci,i = 0 ; wi,i = qi .
Nekünk a T0,n fát kell megkeresni. Vegyük észre, hogy ha tudnánk, hogy
ri,j = k , akkor a szuboptimalitás elve miatt a Ti,j fa gyökerének bal részfája
Ti,k−1 , jobb részfája pedig Tk,j lesz. Ezért ekkor a költsége ci,j = (ci,k−1 + qi +
+ pi+1 + . . . + qk−1 ) + pk + (ck,j + qk + pk+1 + . . . + qj ) = ci,k−1 + ck,j + wi,j lesz,
mivel a Ti,j fában minden csúcs mélysége eggyel nagyobb, mint a Ti,k−1 ill. a
Tk,j fákban. Vegyük észre, hogy a költségben wi,j állandó, nem függ a k -tól.
Ezek alapján könnyen készíthetünk egy rekurzív algoritmust :

R(i, j) : c0i,j := ∞
FOR k := i . . . j
C1 := R(i, k − 1)
C2 := R(k, j)
0 0
IF C1 + C2 < ci,j THEN ci,j := C1 + C2; ri,j := k
0
RETURN(ci,j := ci,j + wi,j )

Ez az algoritmus azért exponenciális, mert ugyanazt a dolgot többször


számoljuk ki. A hívások száma az ún. Catalan-szám lesz. Hogy ezt elkerüljük,
dinamikus programozással fogjuk megoldani a feladatot.

A dinamikus programozás jellemz®i :

57
 Amit már egyszer kiszámoltunk, azt feljegyezzük, hogy ne kelljen még
egyszer kiszámolni, valamint

 jó sorrendben oldjuk meg az egyes részfeladatokat.

a5

a4 a6

q3 q4 q5 q6

Az algoritmus (a fenti Inicializálás után) :

FOR l = 1...n − 1
FOR i = 1...n − l
j := i + l
ci,j := wi,j + mini<k≤j (ci,k−1 + ck,j )
ri,j := argmini<k≤j (ci,k−1 + ck,j )
Id® : O(n3 ).

7.1.2. M¶veletek általános bináris keres®fában


Keresés(x) : lásd fentebb.

Beszúrás(x) : Keresés(x), ha x ∈
/ S, megkapjuk az r ktív levelet és berakjuk
oda x-et. Küls®nél a keresés vagy egy levélben áll le, akkor ahelyett egy bels®
csúcs kell, az eredeti levél és a beszúrandó lesznek a gyerekei. Vagy pedig egy
olyan x csúcsban, melynek pl. nincs jobb gyereke, de a keresés jobbra lépne ;
ekkor x új jobb gyerekébe rakjuk az új elemet.
Törlés(x) :

 Küls® : triviális (megkeressük és kitöröljük), de a testvérét felrakjuk a szü-


l®be (ne legyen egy felesleges útjelz® tábla).

 Bels® :

Két eset lesz :

1. eset : Ha x-nek legfeljebb egy gyereke van, akkor a gyereke jön a helyére.
Ekkor az új fa is jó keres®fa lesz.

58
x
y
y

2. eset : Ha x-nek 2 gyereke van :

 Megkeressük az x-et közvetlenül megel®z® y elemet. Ezt úgy tesszük, hogy


1-et balra lépünk és utána amíg lehet mindig jobbra.

 x-et és y -t felcseréljük, majd az új x-et töröljük az els® módszer szerint


(ennek már nincs jobb gyereke).

xy

Jó az algoritmus : ha töröljük x-et, akkor y már jó helyen lesz. Nem romlik


el a keres®fa tulajdonság.

Ami rossz :

 A lépésszámok legrosszabb esetben a fa mélységével egyenl®ek.

 Nem tudunk semmit a fa mélységér®l !

 Ha például n elemet szúrunk be növekv® sorrendben, akkor a fa mélysége


n lesz.

Cél : Olyan fa kialakítása, amelynek mélysége O(log n).

59
7.2. 2-3 fák
Tulajdonságok :

 Minden csúcsnak 2,3 vagy 0 gyereke lesz (kivéve ha a fa 2 pontú).

 Küls® tárolású lesz a fa

 Minden levél ugyanazon a szinten lesz. Így n≥2 esetén legfeljebb dlog ne
mélység¶ lesz a fa.

 Minden nem-levél csúcsban két útjelz® táblát tárolunk.

Deníció : max(v)= a v alatti levelek kulcsainak maximuma.

max(u1) max(u2)

u1 u2 u3

Tehát egy v csúcsban az els® útjelz® max(u1 ), a második max(u2 ), ha v -nek


u1 az els® és u2 a második gyereke.
Keresés(s) :

 IF u levélben vagyunk THEN

 IF s = max(u) THEN Return(u) ELSE Return(nincs)

 ELSE IF s ≤ max(u1 ) THEN → u1


 ELSE IF s ≤ max(u2 ) THEN → u2
 ELSE → u3

Ez mindig legfeljebb log n lépés lesz.

Beszúrás(s) :

 Megkeressük, hogy hová kell beszúrni s-et, jelölje x az utolsó nem-levél


csúcsot.

60
 Ha x-nek 2 gyereke volt, akkor beszúrjuk és így 3 gyereke lesz (x-ben
átállítjuk az útjelz®ket).

 Ha x-nek 3 gyereke volt, akkor szétszedem x-et 2 darab 2 gyerekes csúccsá.

x1 x2

Ha x szül®jének 2 gyereke volt, akkor nincs gond, ha 3, akkor ezt a csúcsot


is szétvágjuk és így folytatjuk a gyökér felé. Ha eljutunk a gyökérig és a
gyökérnek 3 gyereke volt, akkor a gyökeret is kettészedjük, beszúrunk egy
új gyökeret, és így n® a fa magassága.

Id® : O(log n)
Az útjelz®ket mindkét esetben állítgatni kell.

20. Tétel. Ha az új elem (s) beszúrásától max(v) n®tt, akkor v gyökér, vagy
annak csupa-jobb leszármazottja, azaz minden eddiginél nagyobb elemet szúrtunk
be.

Bizonyítás : Ha egy lépésben nem jobbra mentem, akkor az úgy lehetett, hogy ott
s ≤ m1 vagy s ≤ m2 volt. Érdemes a maximális elem értékét külön is eltárolni.

Következmény : Általában felfelé nem kell átírogatni az útjelz® táblákat.

Példa, amikor át kell írni :

x x1 x2

max(u1) max(u2)

u1 u2 u3
Ennek a maximuma nincs
feljegyezve!

Ekkor nem tudjuk mind a 4 gyereknek a maximumát (most fel kell írnunk). A
hiányzó érték max(x) lesz, de ez is fel van írva a nagyszül®ben, vagy valamelyik
távolabbi ®sben.

61
m2=max(y)

Ha így eljutunk a gyökérig, akkor a max(x) a külön tárolt maximum lesz. Ez


legfeljebblog n darab fellépést és legfeljebb log n darab lefelé lépést jelenthet,
de ekkor az y minden ®sének maximuma is megvan. Könny¶ látni, hogy ha x
gyereke lett az új levél, akkor elég x-t®l a gyökérig vezet® úton lev® csúcsok
maximumát tudni.

gyökér

max(y)

Törlés(s) :

 Megkeressük az s-et tartalmazó levelet és kitöröljük.

 Ha p(s) = x-nek 3 gyereke volt, akkor készen vagyunk (de az útjelz®ket


felfelé javítani kell, max(x) változhatott !).

 Ha nem, akkor 2 esetet különböztetünk meg :

1. Létezik x-nek szomszédos testvére 3 gyerekkel : elkérjük az egyiket (a


felénk es®t).

y x y x

z s z

max(z) fel van írva vagy y -ban, vagy p(x)-ben. Felfelé javítanunk kell
a jelz®táblákat !

62
2. Ha nem létezik, akkor x-et összevonjuk valamelyik testvérével.

y
y

x x

Lehet, hogy ekkor az új x szül®jének lesz csak 1 gyereke, tehát felfelé


folytatni kell. Hogyan ér véget az algoritmus :

 A szül® 3 gyerekes volt, és most kett® maradt neki.

 Feljutunk egészen a gyökérig, töröljük az 1 gyerekes gyökeret (ha


n ≥ 2) és így csökken a fa magassága.

7.3. B-fák
Tulajdonságai :

l m
B
 Minden nem-levél csúcsra igaz, hogy :
2 ≤# gyerekek ≤B (kivéve,
l m
B
ha gyökér és ha n< 2 ).

 Egy csúcsban B−1 útjelz® lesz.

 Minden m¶velet a 2-3 fák mintájára fog menni.

Küls® táraknál használják : Ezek a tárolók 1 olvasás során egy egész lapot
olvasnak be. Az tart sok ideig, míg a fejet a megfelel® lapra pozícionálja a vezér-
lés. Az a cél, hogy minél kevesebb lapot kelljen olvasni ahhoz, hogy megtudjuk,
hogy hol van az az adat, amit a tárolón keresünk. Úgy érdemes B -t úgy meg-
választani, hogy a B−1 útjelz® elférjen egy lapon.

Példa : Ha n = 224 ∼ 16.7 millió és B = 32, akkor a fa mélysége legfeljebb


6 lesz. Ha a fels® 3 szintet a memóriában tároljuk (ez 322 ∼ 1000 lap), akkor
minden m¶velet legfeljebb 4 lapolvasás lesz (azaz legfeljebb 4-szer kell olvasni a
winchestert).

7.4. Piros-fekete fák


Eredetük : Bináris faként tárolt 2-4 fák :

63
f
p p

f
p

Tulajdonságai :

0. Bels® tárolású bináris keres®fa,

1. minden csúcsa piros(p) vagy fekete(f ) ; a ktív levelek feketék,

2. piros csúcs apja fekete,

3. hogy kiegyensúlyozott legyen a fa : akárhogyan megyek le egy csúcsból (x)


egy alatta lev® ktív levélig, az úton mindig ugyanannyi fekete csúcs van.
Ezt x fekete magasságának hívjuk és mf (x)-szel jelöljük.

21. Tétel. Egy piros-fekete fa mélysége legfeljebb 2 · log n.

Bizonyítás : indukcióval, vagy a piros csúcsok fekete szül®be való behúzásával


(ekkor minden ktív levél mélysége ugyanaz lesz), triviális. Érdemes megjegyez-
ni, hogy a fenti feltételek implicite tartalmazzák, hogy ha egy v csúcsnak csak
egy valódi gyereke van, akkor az egy piros levél (v másik gyereke egy fekete ktív
levél, így ha v piros lenne, akkor fekete magassága 1 lenne, de ez csak úgy lehet,
ha másik gyereke is egy ktív levél ; ha pedig v fekete, akkor fekete magassága
2, tehát csak piros valódi leszármazottja lehet).

M¶veletek :
Keresés(x) : Ugyanúgy kell megvalósítani, mint egy tetsz®leges bináris fában.

Egy kiegészít® alapm¶velet :


Billentés(x) :

y=P(x) x
x y

C Billentés(x) A

A B B C

Piros-fekete fáknál ez nem mindig lesz jó. Ha y fekete és x piros, és ekkor még
át is kell színezni x-et és y -t, hogy teljesüljön a 3. tulajdonság. Pl. ha a fa alakja
a következ® :

64
y f x f
p p
f y
x

C Billentés(x) A
f

A B B C

Beszúrás(s) : A beszúrás egy fekete szín¶ x ktív levél helyére történik, alatta
létrejön két új fekete ktív levél. Ezt a csúcsot (x-et) pirosra kell színeznünk a
3. tulajdonság fenntartása miatt.

f p

f f

Akkor van probléma, ha a beszúrt x elem y apja piros szín¶ (ha fekete, akkor
készen vagyunk). Ezen belül három esetet különböztetünk meg :

p
x

f f

1. eset : Ha y testvére piros :

f p

p p f
y f y

x p x p

Ezután x := p(y) és folytatjuk felfelé az eljárást (ez legfeljebb log n lépést jelent).
2. eset : Ha p(p(x)) = z -nek x bal-bal vagy jobb-jobb unokája.

65
f y
f z

p x p z
y p
f
p
x Billentés(y)
D f
C C
A B D
A B

7. Állítás. Egy ilyen billentés után rendben van a fa.

3. eset : Ha nem az 1. vagy 2. eset áll fenn. Ekkor is elég 2 Billentés :

f z
f z f x

y x p
p
f y y Z
p p p
p x Billentés(x) f Billentés(x)
D
A C D
f

A B A B C D
B C

8. Állítás. Egy ilyen dupla billentés után rendben van a fa.

Összefoglalva : Egy Beszúrás(x) legfeljebb log n átszínezés és legfeljebb 2 Bil-


lentés m¶velettel megvalósítható.

Törlés(x) : Itt 5 különböz® eset lesz és belátható, hogy legfeljebb log n átszíne-
zéssel és legfeljebb 3 billentéssel megoldható a m¶velet.

7.5. AVL-fák (Adelszon-Velszkij és Landisz)


Tulajdonságai :

 Bináris keres®fa,

 m(f ) = 0, ha f ktív, ahol m a magasság,

 ∀x : |m(bal(x)) − m(jobb(x))| ≤ 1.

Ezen tulajdonság ellen®rzése és fenntartása csúcsonként 2 bittel biztosítható :


1. a bal gyerek magasabb-e mint a jobb gyerek, illetve 2. a jobb gyerek magasabb-
e mint a bal gyerek.

Könnyen bizonyítható, hogy ha ezek a tulajdonságok teljesülnek egy d mély-


ség¶ fára, akkor ennek legalább Fd+3 − 1 csúcsa van. Ebb®l következik, hogy a
fa mélysége legfeljebb 1,44 · dlog ne lehet.

66
M¶veletek :
Beszúrás(s) : Beszúrjuk s-et, majd megkeressük a legközelebbi x ®sét, ahol a
3. tulajdonság elromlott ; közben persze a fenti jelz®biteket megfelel®en átállít-
juk. Majd itt egy szimpla vagy egy dupla billentéssel a tulajdonság helyreállít-
ható az alábbi módon :

1. eset : s az x bal-bal, vagy jobb-jobb unokájának a leszármazottja

x y

y x
Bill(y)

C A

A B B C

2a. eset : s az x bal-jobb, vagy jobb-bal unokája

x s

Bill(s)
y y x
Bill(s)
s

2b. eset : s az x bal-jobb, vagy jobb-bal z unokájának valódi leszármazottja

x z

y y x
Bill(z)

z Bill(z)
D
B C

A A D
B C
s

Belátható, hogy egy beszúrásnál az x elem helyének megkeresése legfel-


jebb d1,44 · log ne id®t vesz igénybe és az egész m¶velet végrehajtása legfeljebb
d1,44 · log ne bit átállítással, valamint legfeljebb 2 Billentéssel jár. Érdemes meg-
jegyezni, hogy x megkeresését már a Beszúrás el®tt, miközben s helyét keressük,
könnyen elvégezhetjük. Azonban ekkor is az s és x közötti csúcsoknál kell állíta-
ni a biteket. Azonban x felett már nem, mert a billentések után azok magassága
ugyanaz lesz, mint beszúrás el®tt.

Törlés(s) : Legfeljebb d1,44 · log ne billentéssel megoldható (lehet, hogy az


egész fát végig kell billegtetni).

67
7.6. Önkiegyensúlyozó fák (Splay-tree; Sleator és Tarjan)
Itt is bels® tárolású bináris fákat használunk, azonban a kiegyensúlyozottságra
nem teszünk explicit feltételt, amit fent kellene tartani. Helyette id®nként bil-
legtetünk, és majd belátjuk, hogy átlagosan, azaz amortizációs id®ben minden
m¶velet elég gyors lesz  azaz O(log n) idej¶.

M¶veletek :
Dbill(x) : Kétfajta Duplabillentést különböztetünk meg.

(a) Ha x a nagyszül®nek bal-bal, vagy jobb-jobb unokája : Billentés(y), majd


Billentés(x)

x
z

y
y
z
x 1. Billentés(y)
D A
2. Billentés(x) B
C

A B C D

(b) Ha x a nagyszül®nek bal-jobb, vagy jobb-bal unokája : Billentés(x), majd


újra Billentés(x)

x
z

y z
y
x 1. Billentés(x)
D
A 2. Billentés(x) A B C D

B C

Felbillegtet(x) :
WHILE p(p(x)) 6= nil
Dbill(x)
IF p(x) 6= nil THEN Bill(x)

A felbillegtetés ideológiája hasonlít az útfelezésre, ha x mélysége h volt, akkor


Felbillegtet(x) után x minden leszármazottjának mélysége bh/2c-lel csökken.
Az alábbiakban a vessz®s m¶veletek az új m¶veletek, a vessz®tlenek a ha-
gyományos bináris fában végrehajtott m¶veletek.

Keresés'(x) :
Keresés(x)
Felbillegtet(x)

68
Beszúrás'(x) :
Beszúrás(x)
Felbillegtet(x)

Törlés'(x) :
z := p(y), ahol y az x megel®z®je
(y = x, ha x-nek legfeljebb 1 gyereke van)
Törlés(x)
Felbillegtet(z)

Id® : Mindhárom m¶velet tényleges ideje nagyjából kétszerese a Felbillegtet


tényleges idejének. A Lépést úgy választjuk meg, hogy kétszer annyi elemi lé-
pést tartalmazzon, mint egy dupla billentés. Ekkor egy m¶velet tényleges ideje
legfeljebb 2+ a Felbillegtetésnél végrehajtott (dupla vagy szimpla) billegtetések
száma.

Potenciál :
x w(x) := x leszármazottainak
súlya : száma (saját magát is beleértve)
x r(x) := blog
rangja :
P w(x)c
A potenciál : P := r(x)

22. Tétel. AI((Felbillegtet(x)) ≤ 1 + 3 · (r(gyökér) − r(x)) ≤ 1 + 3 · blog nc =


= O(log n).

Bizonyítás: Belátjuk minden egyes billent® lépésre, hogy teljesül a fenti állí-
tás megfelel®je. Összeadva ezeket egy teleszkópikus összeget kapunk, amelyb®l
minden tag kiesik, kivéve 1 + 3 · (r(gyökér) − r(x)). A továbbiakban r jelöli a
billentés el®tti, míg r0 a billentés utáni rangot.

1. Szimpla billentés : (y = p(x) gyökér) :

AI ≤ 1 + 3 · (r(y) − r(x)).

y x

x y

1. Billentés(x)
C A
A B B C
r r'

∆P = r0 (x) + r0 (y) − r(x) − r(y) = r0 (y) − r(x) ≤ r(y) − r(x) ≤ 3 · (r(y) −


− r(x)), mivel r0 (x) = r(y) és r0 (y) ≤ r(y) és r(y) ≥ r(x). Tehát AI = 1 +
+ ∆P ≤ 1 + 3 · (r(y) − r(x)).

2. (a) típusú Duplabillentés :

Be kell látnunk, hogy ˙


AI ≤ 3(r(z) − r(x)).

69
∆P = r0 (z) + r0 (y) + r0 (x) − r(z) − r(y) − r(x) ≤ 2 · (r(z) − r(x)), mivel
r0 (z) és r0 (y) ≤ r0 (x) = r(z), és r(y) ≥ r(x).
Ha r(z) > r(x) ⇒, ekkor 3 · (r(z) − r(x)) ≥ 2 · (r(z) − r(x)) + 1 ≥ ∆P + T I .
Ha r(z) = r(x) = r (ez az alsó-egészrész miatt lehetséges) ⇒ r(y) = r és
r0 (x) = r.
Elég belátni, hogy r0 (z) < r, mert akkor ∆P ≤ 2r + r0 (z) − 3r ≤ −1, így
AI ≤ 3 · 0, mert ∆P ≤ −1 és T I = 1.

β α
x
z

y
α y
z
x 1. Billentés(y)
A
C
D
2. Billentés(x) B β

A B C D

α és β jelentse az adott részfa csúcsainak a számát.

Ekkor : w(z) = α + β .
Indirekt tegyük fel, hogy r0 (z) = r ⇒ β ≥ 2r . Mivel r(y) = r ⇒ α ≥ 2r ,
r+1 0
innen : α+β ≥2 ⇒ r (x) ≥ r + 1, ami ellentmondás.
3. (b) típusú Duplabillentés :

Minden a 2. ponthoz hasonlóan megy.

x
z
α
α y β
y z β
x 1. Billentés(x)
D
A 2. Billentés(x) A B C D

B C

Most r0 (y) < r vagy r0 (z) < r -et kell megmutatnunk.

Indirekt tegyük fel, hogy r0 (y) = r0 (z) = r ⇒ α ≥ 2r , β ≥ 2r ⇒ r0 (x) ≥


≥ r + 1, ami ellentmondás.

70
Végs® elemzés :
AI(Keresés') ≤ ∆P (Keresés)+∆P (Felbillegtet)+2+T I(Felbillegtet) (mivel
T I(Keresés) belefér a Felbillegtet tényleges idejébe). Így, mivel ∆P (Keresés) =
= 0, ezért AI(Keresés') = O(log n).
AI(Törlés') ≤ ∆P (Törlés) + ∆P (Felbillegtet) + 2 + T I(Felbillegtet) (mivel
T I(Törlés) belefér a Felbillegtet tényleges idejébe). Így, mivel ∆P (Törlés) < 0,
ezért AI(Törlés') = O(log n).

AI(Beszúrás') ≤ ∆P (Beszúrás) + ∆P (Felbillegtet) + 2 + T I(Felbillegtet)


(mivelT I(Beszúrás) belefér a Felbillegtet tényleges idejébe). Így, ha belátjuk,
hogy ∆P (Beszúrás) = O(log n), akkor AI(Keresés') = O(log n).

x beszúrásakor w(y) csak akkor n®, ha x az yleszármazottja. r(y) csak


akkor n®, ha y a gyökérb®l x-be vezet® úton van és w(y) = 2k − 1 alakú volt.
Mivel az úton felfelé w(y) szigorúan n® ⇒ csak ≤ blog nc ilyen lehet (k =
= 1 . . . blog nc) ⇒ ∆P (Beszúrás) ≤ log n.

Összegezve : Minden m¶velet amortizációs ideje O(log n).


Ez az adatstruktúra nagyon jól alkalmazható például a Szétvág(S, a) m¶ve-
letre is : a-t felbillegtetjük és a két részfa lesz a megoldás.

8. Hashelés

Cél : A szótárm¶veleteket akarjuk megvalósítani úgy, hogy átlagosan jó meg-


oldást kapjunk. Ehhez el®ször is egy olyan h : U → [0,1 . . . M − 1] függvényt
keresünk, amely az x ∈ U elemekre ad egy címet. A célunk az, hogy ezen a
címen tároljuk x-et.

M jelentse a táblaméretet, N az aktuálisan benne tárolt adatok számát és


N
α := M .


Születésnap paradoxon : Ha N ≥ ln 4 · M , akkor legalább 21 valószín¶séggel
lesz olyan x 6= y , hogy h(x) = h(y). Ezt ütközésnek fogjuk nevezni.

h függvény egyenletesen terítse szét az inpu-


Ennek ellenére az a cél, hogy a
1
tokat, vagyis Pr (h(x) = i) =
M legyen minden i < M számra. Itt a valószín¶ség
az input kulcsokon értend®, amelyeken egy (általában el®ttünk csak körül-belül
ismert) eloszlás van, amely szerint érkezni fognak. Az elemzésekben fel fogjuk
1
tenni, hogy sikerült olyan hash függvényt találni, amelyre a Pr (h(x) = i) = M
feltétel teljesül.

Két dolgot fogunk vizsgálni : hogyan keressünk jó hash függvényt és hogyan


kezeljük az ütközéseket.

71
Persze mindenekel®tt az U univerzum elemeit valahogyan természetes szá-
mokra kell leképeznünk. Ez általában könyen megoldható úgy, hogy különböz®
kulcsoknak különböz® számok feleljenek meg. Ezt a továbbiakban feltételezzük,
tehát innent®l úgy tekintjük, mintha U elemei természetes számok volnának.

8.1. Hash függvények


1. Osztó-módszer :

h(K) = Kmod M , ahol K a kulcs (egy szám) és M egy prím.

2. Szorzó-módszer :
n 
A o
h(K) = ·K ·M , ahol {} a törtrész, M = 2m és W = 2w alakú.
W
Ezért a W -vel való osztás, a törtrész, az M -el való szorzás és az egész-
rész bitléptetésekkel/levágásokkal gyorsan megvalósítható ; igazából csak
az A·K szorzás kiszámítása igényel lényeges id®t.

A
A megválasztása : az lenne a legjobb választás, ha W = α irracionális lenne
(α < 1).

Megjegyzés : {α · K} ∈ [0; 1).


Szemléltetés : egy egységnyi kerület¶ körön K -szor mérem fel egymás után
α-t.

2α 8α
α


4α 6α

23. Tétel. (T. Sós Vera) : Ha α irracionális, akkor a szomszédos pontok


közötti távolságok összesen 3 félék lesznek és a következ® ((K + 1) · α) a
legnagyobb ívet osztja ketté.

5−1
Ezen belül a legegyenletesebb akkor lesz a felosztás, ha
2 , ekkor α=
a legnagyobb intervallumot a következ® elem az aranymetszés arányában
fogja felosztani.

A 5−1
Ezek alapján A megválasztása : úgy válasszuk A-t, hogy W ∼ 2 legyen.

72
8.2. Ütközések feloldása
8.2.1. Láncolt hash-elés

L
0

f(K)=i nil
nil

M-1
LISTAFEJEK

A láncolt lista elemei :

 egy-egy teljes rekord, vagy

 mutató a rekordra és a rekord kulcsa

Keresés(K) : az h(K)-dik vödröt végigjárjuk.

Beszúrás(K) :

 Ha tudjuk, hogy nincs a táblában, akkor a lista elejére szúrhatjuk (1 lépés).

 Ha nem tudjuk, akkor végigjárjuk a listát és ha nem találtuk akkor a


végére beszúrjuk.

Törlés(K) : egyszer¶.

Id® :
c0N : a sikertelen keresés várható lépésszáma, ahol f (K) = i 1
M valószín¶ség-
gel.

1
cN : a sikeres keresés várható lépésszáma, ha minden elemet
N valószín¶ség-
gel keresünk.

N
Emlékeztet® : α= M.

Legyenek k0 , k1 , . . . kM −1 a láncok hosszai, nyilván ezek átlaga α.

73
M −1 P
X 1 ki X 1 N
Ekkor : c0N = (ki + 1) = + = + 1 = α + 1.
i=0
M M M M

A ξi valószín¶ségi változó jelentse azt, hogy az i. elem beszúrásához hány


lépést tettünk meg (a keresése is ennyi lépés lesz).

E(ξi ) = c0i−1 cN = E( N1 ξi ) = 1 1
c0i−1
P P P
Ekkor : és
N E(ξi ) = N a várható
érték linearitása miatt.
N N N ·(N −1)
X 1 i−1 1 X i−1 2
Innen : cN =
(1 + ) = 1+ ( ) = 1+ = 1+
i=1
N M N i=1 M NM
N −1 α
+ ≈1+ .
2M 2

A láncolt hash-elés el®nyei :

 egyszer¶,

 mindig tudunk új elemet berakni, azaz nem telik be (amíg tudunk helyet
foglalni a memóriában),

 a nagyon sok elemet tartalmazó vödörbe nem megy nagyobb valószín¶ség-


gel elem, mint egy üres vödörbe,

 könny¶ törölni,

 ha például α ∼ 1, akkor cN ≈ 1,5 és c0N ≈ 2,


 küls® táras megoldásként kifejezetten jó. Pl. ha egy lapra 10 rekord fér és

α-t 8-nak választjuk, akkor a lapelérések várható száma sikeres keresésnél

1.4, beszúrásnál 1.8 alatt marad.

Hátrányai :

 nagy memóriaigény (M +N darab pointer kell),

 közben kell memóriát allokálni.

8.3. Nyílt címzés


A nyílt címzéses módszerekben közös, hogy minden módszer esetén egy x táblát
használunk (tömböt) és feltesszük, hogy N ≤ M −1 (mindig hagyunk egy üres
helyet).

74
0

M-1

A hash függvény : h : U → [0, M − 1] mellett több másik hash függvényt is


használunk : hi : U → [1, M − 1], ahol i = 1, . . . M − 1 és h0 = 0.

A következ® keresési sorozatot vizsgáljuk :


h(K)−h0 (K), h(K)−h1 (K), . . . h(K)−hM −1 (K) mod M , ahol a h1 (K), h2 (K), . . . hM −1 (K)
sorozat az 1,2, . . . M − 1 egy permutációja.

A hash-elés menete : kiszámítjuk a tagokat addig, amíg nem lesz üres az


adott x tábla adott index¶ eleme.

Beszúrás : a sorozat els® üres helyére

Keresés : a sorozat szerint sorban keresünk (csak az els® üres helyig kell
keresni)

Megjegyzés :

 A nyílt címzés név onnan származik, hogy nincs el®re eldöntve, hogy hová
rakom az egyes elemeket.

 Annál jobb lesz az eredmény minél "véletlenebb" a permutáció.

8.4. Lineáris hash-elés


A második hash függvény : hi := i. Ha a tömbben elérünk az els® elemig, akkor
hátulról indulunk tovább. Ez magyarázza azt, hogy a hash függvényben kivonás
szerepel összeadás helyett (a 0-val gyorsabban tudunk összehasonlítani).

h(K)

75
El®ny :

 Gyors és egyszer¶.

Hátrányok :

 Ha kialakul egy hosszú telített rész (lánc ), akkor nagy annak a valószín¶-
sége, hogy a következ® elem is ebbe a láncba érkezik,

 A láncok össze tudnak n®ni.

Lépések : cN = 12 (1 + 1
1−α ) és c0N = 21 ((1 + 1 2
1−α ) ).

cN és c0N néhány értéke :

1 2
α 2 3 0,8 0,9
cN 1,5 2 3 5,5
c0N 2,5 5 13 50,5

Ennél a módszernél még lehet törölni.

Törlés : Az okoz gondot, ha a kés®bbiekben nem találjuk meg K 0 -t, azaz K0


0
a törölt elem felett (ciklikusan értend® !), h(K ) pedig alatta van.

K'

K1
törölt
h(K')

Megoldás : Elindulunk a törölt elemt®l felfelé. Ha gondot okozó K0 elemet ta-


0
lálunk, azt nyugodtan átrakhatjuk a törölt elem helyére, és K eredeti helyét
tekintve törölt helynek, rekurzívan folytatjuk az eljárást. Ha elérünk egy üres
helyet, akkor nyugodtan megállhatunk, mindent rendbe raktunk.

8.5. Dupla hash-elés


Csak egy újabb hash függvényt keresünk, melyre h0 (K) ∈ [1, M − 1] és mindig
0
relatív prím M -hez. Legyen hi (K) = i · h (K).

Ez a módszer akkor lesz igazán jó (amit a további elemzésekben fel is te-


 
szünk), ha Pr h(K1 ) = h(K2 ) és h0 (K1 ) = h0 (K2 ) ≈ 1
M 2 teljesül minden
K1 6= K2 -re.

76
Osztó-módszer : M 0 és M legyenek ikerprímek és h0 (K) := 1+(b M
K
cmodM 0 ).
0
A +1 azért kell, hogy i · h ne legyen 0.

Szorzó-módszer : Jelölje a az AK szorzat következ® m bitjét. h0 (K) := a OR


1 (az utolsó bitjét 1-re állítjuk, így nem lesz 0 és biztosan relatív prím lesz a
kett®-hatvány M -hez).

A*K
h(K) a 1

m m

Lépések : cN = 1 1
α (ln 1−α ) és c0N = 1
1−α .

cN és c0N néhány értéke :

1 2
α 2 3 0,8 0,9
cN 1,39 1,65 2,01 2,56
c0N 2 3 5 10

Megjegyzés : Ez egy elég jó módszer (ha h és h0 teljesítik a feltételeket), ennél


sokkal jobbat nem is igen várhatunk.

Törlés dupla hash-elésnél : Az el®z® trükköt itt nem alkalmazhatjuk. Amikor


törlünk egy elemet, akkor egy speciális szimbólumot helyezünk el a helyére,
ami azt jelöli, hogy az adott elem törölt. A beszúrásnál ide beszúrhatunk, de
a keresésnél tovább kell lépnünk. Az a probléma, hogy sok törlésnél gyorsan
megtelik a tábla ilyen szimbólumokkal.

8.6. A dupla hash-elés Brent-féle változata


Ennél a módszernél cN → 2,49, ha α → 1. A beszúrásnál némi plusz munkát
végzünk, hogy a majdani kereséseket gyorsítsuk. Jó pl. program fordítása során
szimbólumtábla építésére.

Ötlet : Ha a beszúrandó K kulcsot be tudom rakni az els® vagy a második


helyre, akkor berakom. Különben sima berakásnál az ® keresése már legalább
három lépés lenne. Legyen a h(K) helyet elfoglaló kulcs K1 . Ha K1 -et eggyel
hátrébb tudom rakni a keresési sorában, akkor K1 keresésén 1-et rontok, de
legalább 2-t javítok K keresésén.

Az algoritmus ez után is mohó módon folytatódik. Ha K1 -et nem sikerült a


h(K) − h0 (K1 ) K -t próbáljuk a h(K) − 2h0 (K) helyre
helyre rakni, akkor utána
0
rakni, ha ez se megy, akkor K1 -et a h(K)−2h (K1 ) helyre és K -t a h(K) helyre,
0
ha ez se megy, akkor K2 -t (aki a h(K) − h (K) helyet foglalja) próbáljuk eggyel
0 0 0
hátrébb rakni a h(K) − h (K) − h (K2 ) helyre és K -t berakjuk a h(K) − h (K)
helyre, és így tovább.

77
8.7. Univerzális hash-elés
Ötlet : Vegyünk sok hash függvényt és ezekb®l válasszuk egyet.

H → [0, M − 1] : hash függvények halmaza. H univerzális, ha ∀K1 6= K2 ∈ U


1

-ra : P rh∈H h(K1 ) = h(K2 ) = M .

Konstrukció : Feltesszük, hogy M prím, és K -t felírjuk M alapú számrend-


szerben :
Pr
K= i=0 xi ·M i , ahol r-et úgy választjuk, hogy K < M r+1 minden kulcsra.

Legyenek a0 , a1 , . . . ar ∈ [0, M − 1] véletlenek és függetlenek.


Pr
ha0 ,...ar (K) := i=0 ai · xi mod M .

24. Tétel. Ez univerzális, vagyis P r


P P 
ai · xi mod M = ai · yi mod M =
1
=M , ha legalább egy j -re xj 6= yj .

Bizonyítás : ha K1 6= K2 (és xi reprezentálja K1 -et, yi pedig K2 -t), akkor


valóban ∃j , hogy xj 6= yj .
Elég belátnunk, hogy ezek feltételes valószín¶sége 1/M minden rögzített
a0 , a1 , . . . aj−1 , aj+1 , . . . ar - re.
Pr
Ha egyenl®ség van, akkor : aj (xj − yj ) ≡ −
i=0,i6=j ai (xi − yi ) mod M .
Mivel xj − yj 6≡ 0 és a jobb oldal a feltételes valószín¶ségben egy rögzített szám,
így egyetlen aj létezik, ami kielégítené a kongruenciát.

9. Geometriai adatstruktúrák

Egy GPS-hez szükséges program alapvet® feladatait fogjuk megvalósítani ebben


a részben.

A feladat : Adott egy x térkép és arra vagyunk kíváncsiak, hogy hogyan néz ki
a térkép egy adott környezetünkben. A kérdés, hogy hogyan érdemes tárolni a
térképet, hogy ezt a feladatot gyorsan meg tudjuk valósítani.

78
Feltételezések (egyszer¶sítések) :

1. a térkép objektumai pontokból és szakaszokból állnak (körökkel nem ér-


demes foglalkozni, mert vagy csak akkor akarjuk kirajzolni, ha a közép-
pontjuk is a téglalapba esik, vagy pedig egy térképen csak 2-3 féle méret¶
kör van, amelyek középpontjait a megfelel®en megnövelt téglalapban ke-
reshetjük),

2. a nevek az objektumokhoz tartoznak, amikor megjelenítünk egy objektu-


mot kiírjuk a hozzá tartozó neveket is,

3. egy pont maximum 6 szakasznak a végpontja,

4. a szakaszok nem metszik egymást.

Megjegyzés : Rengeteg további alkalmazás van, pl. VLSI áramkörök tervezése,


CAD rendszerek, ill. több dimenzióban VR alkalmazások (repül®gép-szimulátor,
játékok), vagy akár adatbázisok bizonyos tulajdonságú elemeinek kilistázása.

9.1. Egy dimenziós feladatok


I. TÁROLNI : x1 , x2 , . . . xn pontokat.

KÉRDÉS : P ON T OK(xb , xj ) - felsorolni azokat a pontokat, amelyek ebbe


az intervallumba esnek, vagyis a válasz : {xi : xb ≤ xi ≤ xj }.
Az output hosszát nem tudjuk el®re. Legyen ezentúl k az output hossza. A
futási id®ket ezentúl k és n függvényében fogjuk vizsgálni.

IDŽ : O(log n + k)
TÁR : O(n)
FELÉPÍTÉS : O(n · log n)
Megoldás : rendezett tömbben tároljuk a pontokat ; xb -t megkeressük felez®
kereséssel, majd innent®l listázunk, míg xj felé nem érünk.

II. TÁROLNI : I1 , I2 , . . . In ⊆ R intervallumok, ahol Ii = [xib , xij ].


KÉRDÉS : IN T ERV (x) → felsorolni Ii -ket, amelyekre x ∈ Ii .
IDŽ : O(log n + k)
TÁR : O(n)
FELÉPÍTÉS : O(n · log n)
Megoldás : intervallum fa. A 2n darab xij , xib közül legyen x0 a középs® (az
n-dik).

Rekurzívan fát építünk :

79
v0 x0,Lb,L j

x0 -tól balra x0 -tól jobbra


lévõ elemek lévõ elemek

 a gyökérben raktározzuk el összes x0 -t tartalmazó intervallumot. Ezek


I(v0 ) = {Ii | xib ≤ x0 ≤ xij }.
 a bal részfába azokat az elemeket, amelyek szigorúan balra vannak az x0 -
n
tól, vagyis : {Ii | xij < x0 }. Itt legfeljebb
2 intervallum lesz.

 a jobb részfában a szigorúan jobbra lév® intervallumok lesznek.

 a két részfát rekurzívan ugyanígy építjük fel.

x x0

Tegyük fel, hogy az IN T ERV (x) kérdésnél pl. x ≤ x0 . Az x-et tartalmazó


intervallumok kétfajták lehetnek :

 azok, amelyek nem érik el x0 -t  ezek a bal részfában lesznek,

 azok, amelyek elmennek x0 -ig  ezeket a gyökérben tároltuk.

Megoldás : A v0 csúcsban x0 értéke mellett I(v0 )-t is tároljuk, mégpedig


Lb (v0 ) és egy Lj (v0 )
kétszer, egy listában.

Lb (v0 ) : x0 -t tartalmazó intervallumok bal végpont szerinti növekv® sorrend-


ben.

Lj (v0 ) : x0 -t tartalmazó intervallumok jobb végpont szerinti csökken® sor-


rendben.

Ha x < x0 : Lb felsorolása az els® elemét®l, amíg a bal végpont ≤ x.

Lépésszám v0 -ban : 1 + k0 , ha k0 darab intervallum található itt, amiben


benne van x is. Ha x < x0 : rekurzívan folytatom a bal fában. A fa mélysége 
a felezés miatt  legfeljebb log n lesz.

A felépítéshez hint : az elején rendezzük az intervallumokat három tömbbe :


összes végpont szerint, bal végpont szerint és jobb végpont szerint. Egy adott
csúcsnál el®ször ezeket lineáris id®ben szétszedjük három részre, utána Lb és Lj
már könnyen kinyerhet®.

80
9.2. Két dimenziós feladatok
III. TÁROLNI : p1 , p2 , . . . pn ∈ R2 pontok, pi = (xi , yi ).
KÉRDÉS : Egy adott [xb , xj ]×[ya , yf ] téglalapba es® pontok felsorolása, azaz
{pi | xb ≤ xi ≤ xj és ya ≤ yi ≤ yf }.

Megoldás : Tartomány-fa (range tree). Els® lépés :

 xi koordináták szerint kiegyensúlyozott küls® tárolású bináris keres®fát


építünk,

 a levelekre pi -ket írjuk fel.

Ha v a fa egy csúcsa, akkor ASSOC(v) := {pi | v alatti levélben van}.

Ezeket egy-egy L(v) tömbben tároljuk, mely az ASSOC(v) elemeit tartal-


mazza y koordináták szerint rendezve.

TÁR : O(n · log n) (egy pont minden szinten pontosan egyszer szerepel).

Válaszoló algoritmus : El®ször xb és xj keresése.

x b keresése x j keresése

? ?

Legyenvsplit az a csúcs, ahol a két keresés elágazik. Egy csúcs fontos, ha


a vsplit -t®l
az xb keresésekor elért levélbe vezet® úton lév® olyan csúcs jobb
gyereke, ahol balra léptünk, illetve, ha a vsplit -t®l az xj keresésekor elért levélbe
vezet® úton lév® olyan csúcs bal gyereke, ahol jobbra léptünk. Könny¶ látni,
hogy a kilistázandó pontok mind vagy ASSOC(v)-ben vannak egy fontos v
csúcsra, vagy a két elért levél valamelyikében. Tehát minden fontos v csúcsban
ki kell válogatni ASSOC(v) elemeib®l a kérdésnek megfelel®eket, majd a két
elért levélben lev® pontot leellen®rizni, hogy jók-e.

81
A fa mélysége log n, így legfeljebb 2 · log n fontos csúcs van.

Minden fontos v -re ya -t megkeressük felez® kereséssel L(v)-ben és kilistázzuk


a pontokat yf -ig.
Egy darab v -nél legfeljebb log n + 1 + kv lépés kell, ahol kv a v -nél kilistázott
elemek száma.

IDŽ : O(log2 n + k)
FELÉPÍTÉS : O(n · log n) Hint : rendezzük a pontokat x és y koordináta

szerint is, megfelel® kereszt-linkelésekkel, ekkor egy csúcsnál a listák lineáris

id®ben kettészedhet®k.

Megjegyzés :

 ez az algoritmus m¶ködik több dimenzióban is, úgy, hogy d dimenzióban az


id® O(logd n + k) lesz és L(v) rekurzívan egy d − 1 dimenziós tartományfa.

Kérdés : az O(log2 n + k) id®t lehet-e csökkenteni ?

A cél : O(log n + k) keresési id®, ehhez az kell, hogy a v -nél csak O(1 + kv )
id®t töltsünk.

9.2.1. Kaszkád tárolás


A és B rendezett tömböket szeretnénk tárolni, ahol A ⊇ B.

A 3 10 19 23 30 37 59 62 70 80 100

H nil

B 10 19 30 62 70 80

H(i) := min{l | B(l) ≥ A(i)}, illetve nil, ha nem létezik ilyen l .

Ha például ya = 20, akkor megkeressük az A-ban az els® ennél nagyobb-


egyenl® elemet (ez A(4) = 23), és H(4) megmutatja, hogy a 30 lesz a B -ben az
els® megfelel® elem.

ASSOC(v) = ASSOC(b(v)) ∪ ASSOC(j(v)).


Kaszkád tárolásnál minden csúcsban az L(v) mellett egy Hb (v) és Hj (v)
pointertömböt is tárolunk.

82
ya -t csak a gyökérnél keressük, utána Hb vagy Hj mutatja meg a "helyét"
(az els® ya -nál nem kisebb elemet).

IDŽ : O(log n + k)

TÁR : O(n · log n)

FELÉPÍTÉS : O(n · log n) Hint : el®ször rendezzük a pontokat egy tömbbe


x szerint, egy másikbay szerint. Ezután felülr®l lefelé konstruálunk, id®nként
kettészedünk egy listát és Hb ill. Hj tömböket töltünk fel közben. Minden megy

a tárhelyben lineáris id®ben.

9.3. Egy téglalapot metsz® szakaszok lekérdezése


Visszatérve az alapfeladatunkra : a kérdezett téglalapba (ablak) es® pontokat le
tudjuk kérdezni, ha tartomány-fában tároltuk ®ket. Hasonló a helyzet az összes
olyan szakasszal, amelynek legalább egyik végpontja az ablakba esik, itt csak
két dologra kell vigyázni :

 a szakaszok végpontjaival együtt tároljuk el a szakasz sorszámát is,

 ha egy szakasz mindkét végpontja az ablakban van, akkor csak egyszer


írjuk ki.

De persze lehetnek olyan, a téglalapot metsz® szakaszok is, melyeknek mind-


két végpontjuk kívül van. El®ször azt a speciális esetet oldjuk meg, amikor min-
den tárolt szakaszunk vízszintes vagy függ®leges, és csak a következ® fejezetben
kezeljük a ferde szakaszok esetét (pl. VLSI tervezésnél ilyenek nincsenek is).
Tehát most azokat a vízszintes és függ®leges szakaszokat szeretnénk kiíratni,
melyek teljesen átmetszik az ablakot. Az ilyenek vagy vízszintesek és metszik a
jobb oldalt, vagy függ®legesek és metszik a fels® oldalt. Ez a két feladat nyilván
ugyanaz, tehát elég az els® esetet kezelni. Azonban újra gyelni kell arra, hogy
most ki fogjuk válogatni az összes vízszintes szakaszt, mely metszi a jobb oldalt,
tehát azokat is, amelyek bal végpontja benne van az ablakban  ezeket azonban
már egyszer kiírtuk, nem szabad újra.

83
Hint a kétszeri kiírás megakadályozására : egyszer¶, de kicsit kényelmetlen
megoldás lehet, ha egyrészt felveszünk egy n hosszú tömböt (n a szakaszok
száma) és egy sort. Ha bármely fázisban megtaláljuk az i. szakaszt, akkor meg-
nézzük, hogy a tömb i. eleme 1-e, ha igen megyünk tovább, ha nem, akkor egyre
állítjuk és i-t berakjuk a sorba. A végén a sorból ki tudjuk íratni az összes
megtalált szakaszt, és közben a tömböt újra le tudjuk nullázni.

IV. TÁROLNI : Vízszintes szakaszok a síkon.

KÉRDÉS : METSZŽ ([x] × [ya , yf ]).


VÁLASZ : felsoroljuk az összes olyan vízszintes szakaszt, mely metszi a kér-
dezett függ®leges szakaszt.

Intervallum-fát készítünk az x tengelyre es® vetületek szerint. DE az eredeti


megoldással ellentétben az I(v) halmazokat nem két listában tároljuk, hanem
helyette két kupacos keres®fában (lásd alább). Az els®ben az intervallumokat
a bal, a másodikban a jobb végpontjuk szerint. A keresés majdnem ugyanúgy
megy, mint a hagyományos intervallum-fában, csak amikor az adott v csúcs I(v)
halmazából ki akarjuk válogatni a minket érdekl®ket, akkor pl. x < x0 esetén
az els® kupacos keres®fában keressük meg azokat, akiknek a bal végpontja a
(−∞, x] × [ya , yf ] végtelen téglalapba esik.

yf

ya

Tehát a következ® feladatot kell még megoldanunk, melyre a tartomány-


fa is megoldás lenne, de most egy még hatékonyabb megoldást adunk (kisebb
tárhellyel).

9.3.1. Kupacos keres®fák


V. TÁROLNI : p1 , p2 , . . . pn ∈ R2
KÉRDÉS : PONTOK((−∞, x] × [ya , yf ]).

84
yf

ya

xj

Megoldás : Kupacos keres®fa  a legszebb öszvér adatstruktúra.

El®ször egy kis el®készületre van szükségünk, egy új m¶veletet vizsgálunk


meg kupacokban. Tehát adott egy bináris kupac, és egy K kulcs, a feladat pedig
az, hogy soroljuk fel az összes olyan v csúcsát a kupacnak, amelyre K(v) ≤ K .

megoldás : REP ORT (v0 , K) hívása, ahol v0 a kupac gyökere.

REP ORT (v, K)


IF K(v) ≤ K THEN P RIN T v ; REP ORT (b(v), K) ; REP ORT (j(v), K)

25. Tétel. Ez O(1 + k) id®ben fut.

Bizonyítás : Ha meghívtuk REP ORT (u, K)-t, akkor vagy u gyökér volt, vagy
u része az outputnak, vagy p(u) része az outputnak, tehát k hosszú output esetén
legfeljebb 3k + 1 hívás lesz.

Megjegyzés : Ez persze csak akkor igaz, ha nem rendezve kellenek az elemek,


különben mindenhol Mintörlés kellene.

A Kupacos keres®fa egy kiegyensúlyozott bináris fa, amelynek minden v csú-


csában egy q(v) pont és egy y(v) útjelz®tábla van.
Ez az öszvér adatstruktúra egyszerre lesz x koordináta szerint kupac, és y
koordináta szerint bináris keres®fa ; pontosabban csak annak hasonmása, azt a
fontos keres®fa tulajdonságot fogja teljesíteni, hogy egy adott v csúcsnál a bal
részfában csupa olyan pontot tárolunk, melyek y ≤ y(v), a jobb
koordinátája
részfában pedig csupa olyan pontot tárolunk, melyek y koordinátája > y(v) ,(de
magában a v csúcsban tárolt q(v) pontnak semmi köze nem lesz y(v)-hez).

Felveszünk egy v0 gyökeret, q(v0 ) := a legbaloldalibb pont.

y(v0 ) := y 0 , amelyre S = {p1 , p2 , . . . pn } \ {q(v0 )} halmaznak a fele van az


y = y 0 egyenes felett. Sb = {pi ∈ S | yi ≤ y 0 } és Sj = {pi ∈ S | yi > y 0 }. Ezeket
rekurzívan ugyanígy tároljuk a gyökér bal ill. jobb részfájában.

85
rekurzívan y' rekurzívan y'
alatti nem q(v) feletti nem
pontok q(v) pontok

A Keresés így megy : el®ször ya -t és yf -et keressük a fában.

belsõ lelógó
elemek: ezek y
szerint csupa jó
pontok

ya yf

A keresési utak során bejárt csúcsokban tárolt q(v) pontokat egyesével leel-
len®rizzük, hogy benne vannak-e a kérdezett téglalapban. Ezeken kívül minden
más jó pont valamely fontos csúcs alatt lesz. Ráadásul egy v fontos csúcs alatti
minden pont y koordinátája az [ya , yf ] intervallumba esik, így a fontos csúcsok
részfáit már kupacként kezelhetjük, és az el®bb látott módon ki tudjuk írni a jó
pontokat O(1 + kv ) id®ben, ahol most kv az output összes, a v részfájába es®
pontjainak számát jelöli.

IDŽ : O(log n + k)
Ez ugyanolyan gyors, mint a tartomány-fa kaszkádolással, de egyszer¶bb a
megvalósítás és kisebb a tárigény :

TÁR : O(n)
FELÉPÍTÉS : O(n · log n)

9.4. Ferde szakaszok lekérdezése


Ami hátravan, az az ablakot teljesen átmetsz® ferde szakaszok lekérdezése. Ezt
négy részben valósítjuk meg, külön-külön lekérdezzük a téglalap négy oldalát

86
metsz® ferde szakaszokat. Szimmetria okokból nyilván itt is elég lekérdezni egy
adott függ®leges szakaszt metsz® ferde szakaszokat.

yf

ya

Megjegyzés : Most el®ször fogjuk kihasználni, hogy a szakaszok nem metsz-


hetik egymást.

Megoldás : A vetületeket most is mint intervallumokat tároljuk. Azonban erre


a célra az intervallum-fa jelenleg nem felel meg nekünk, ezért egy újabb, els®
ránézésre sokkal rosszabb megoldást kell adnunk erre az egy dimenziós feladatra.

9.4.1. Szakasz-fa
Felbontjuk a számegyenest a végpontok által meghatározott egypontú és nyílt
szakaszokra. Egy-egy ilyen szakaszba azok az x-ek esnek, amelyekre biztosan
ugyanaz a válasz (az ®t tartalmazó intervallumok halmaza).

Összesen legfeljebb 4n + 1 darab ilyen szakasz lesz : legfeljebb 2n darab 1


pontú és legfeljebb 2n + 1 darab nyílt szakasz (két tárolt intervallum végpontjai
egybe is eshetnek).

Egy kiegyensúlyozott bináris keres®fát építünk, melynek leveleire ráírjuk a


szakaszokat úgy, hogy balról jobbra rendezve legyenek. A fa egy v csúcsára de-
niáljuk egyrészt azInt(v) intervallumot, mely a v alatti levelekre írott szakaszok
uniója ; másrészt az ASSOC(v) := {Ij | Ij ⊇ Int(v), de Ij 6⊇ Int(p(v))} hal-
mazt. A v csúcsnál tároljuk ASSOC(v)-t mégpedig egy tömbben (itt még van
némi szabadságfokunk, hogy milyen sorrendben, ezt a következ® alfejezetben ki
is fogjuk használni).

87
v

-) . () .() .() .

26. Tétel. Egy adott Ij intervallumot a fa egy szintjén maximum kétszer tárol-
juk, így az összes tárigény O(n · log n).

Bizonyítás : Ha Ij egy szinten háromszor lenne tárolva, akkor legyenek balról


jobbra a, b, c a csúcsok, amiben tároltuk. Mivel Int(a) ⊆ Ij és Int(c) ⊆ Ij , ezért
a keres®fa tulajdonságai miatt nyilván b szül®jére Int(p(b)) ⊆ Ij , tehát Ij nem
lehet benne ASSOC(b)-ben.

Keresés : a fában itt is x-et keressük, és a keresési út során érintett minden v


csúcsra a teljesASSOC(v)-t kilistázzuk. Tehát O(log n + k) id®ben itt is megy.
A szakasz-fa el®nye az intervallum-fához képest, hogy a csúcsokban tárolt
ASSOC(v) halmazok uniója pontosan az, amit ki kell adnunk, ebb®l itt nem
kell válogatni. Pont ezért, ha az ASSOC(v) tömböket jól rendezzük, akkor még
egy másik szempont szerint tudunk majd bel®lük szelektálni.

9.4.2. Egy függ®leges szakaszt metsz® ferde szakaszok


A szakaszokat az x tengelyre es® vetületük szerint szakasz-fában tároljuk, és
ebben keressük x-et. A keresés során a fa egy adott v csúcsánál ASSOC(v)-
ben azok a ferde szakaszok vannak, amelyek vetülete tartalmazza Int(v)-t, de
nem tartalmazza Int(p(v))-t. Ezek tehát mind átmennek az Int(v) × (−∞, ∞)
függ®leges végtelen csíkon. Mivel nem metszik egymást, így alulról felfelé egyér-
telm¶en rendezhet®k, tehát eszerint tároljuk ®ket az ASSOC(v)-t tartalmazó
tömbben. Kereséskor felez® kereséssel könnyen megtalálhatjuk a legalsó olyat,
mely metszi a kérdezett [x]×[ya , yf ] szakaszt, majd ett®l kezdve addig íratjuk ki
ASSOC(v) elemeit, míg metszik ezt a kérdezett szakaszt. A v -nél eltöltött id®
2
így O(log n + kv ), tehát az összes keresési id® O(log n + k). Az egész struktúra
felépíthet® O(n log n) id®ben.

88
x
Int(v)

89