450 likes | 571 Views
Diskrétne geometrické štruktúry. 2. Martin Florek florek@sccg.sk www.sccg.sk/~florek. Bin árny prehľadávací strom. Bin árny prehľadávací strom – zložitosť na základe výšky stromu Potreba zabezpečiť čo najmenšiu výšku stromu Pre kompletný binárny strom s výškou h je počet vrcholov 2 h – 1
E N D
Diskrétne geometrické štruktúry 2. Martin Florek florek@sccg.sk www.sccg.sk/~florek originál Martin Samuelčík, zmeny Martin Florek
Binárny prehľadávací strom • Binárny prehľadávací strom – zložitosť na základe výšky stromu • Potreba zabezpečiť čo najmenšiu výšku stromu • Pre kompletný binárny strom s výškou h je počet vrcholov 2h – 1 • Cestu možeme skrátiť až na lg(n+1) • Potreba vyváženosti v každom vrchole
AVL stromy • Adelson-Velskii a Landis • AVL strom je samovyvažujúci sa BPS • pre každý jeho vrchol v platí: • buď v je list • alebo v má jedného syna, ktorý je list • alebo v má dvoch synov. Ak ľavý podstrom vrchola v má výšku h1 a pravý h2, potom platí:h1 – h2 1 • b(v) = h1 – h2 • lg(n+1) h 1,44*lg(n+2)
Vkladanie prvku do AVL stromu • Najprv Insert pre BPS • V niektorých vrcholoch môže vzniknúť nevyváženosť • Vrchol x treba vyvažovať práve vtedy, keď platia nasledujúce dve podmienky: • b(x) = 1 a nový vrchol je pridaný doľava od x, • b(x) = -1 a nový vrchol je pridaný doprava od x, • pre y, ktorý leží na ceste od x ku pridávanému vrcholu platí: b(y) = 0
Vyvažovanie • Podstrom s nevyváženým vrcholom treba preskupiť aby nastala vyváženosť • Treba zachovať pravidlá BPS • Treba zachovať výšku podstromu, aby sa nemuseli vyvažovať ostatne vrcholy • Viaceré konfigurácie - rotácie
AVL_INSERT(T,v) • { • x = T->root; • if (T->root == NULL) {T->root = v; v->b = 0;} • else • { • INSERT (T->root, v); • MODIFY (x, v); • if (x->b == 2) • { • if (x->left->b == 1) vyvažuj podľa A • if (x->left->b == -1) vyvažuj podľa B • } • if (x->b == -2) • { • Symetricky ku (b(x) == 2); • } • } • } AVL – algoritmy • MODIFY (u, v) • { • if (v->key < u->key) • if (u->left!= NULL) • { • u->b = u->b + 1; • MODIFY (u->left, v); • } • if (v->key > u->key) • if (u->right != NULL) • { • u->b = u->b – 1; • MODIFY (u->right, v); • } • } • struct AVLTree • { • AVLNode* root; • } • INSERT (u, v) • { • if (v->key < u->key) • if (u->left== NULL) {u->left = v;if (u->b != 0) x = u;} • else • { • if (u->b != 0) x = u; INSERT (u->left, v); • } • if (v->key > u->key) • if (u->right== NULL) { u->right = v; if (u->b != 0) x = u;} • else • { • if (u->b != 0) x = u; INSERT (u->right, v); • } • } • struct AVLNode • { • int key; • AVLNode* left; • AVLNode* right; • AVLNode* parent; • int b; • void* data; • }
Rotácie LL rotation RR rotation
Rotácie 2 LR rotation RL rotation
Vymazanie vrcholu z AVL • Rozšírenie DELETE pre BPS: • 1)v má najviac 1 syna: • vrchol v vynecháme ako pri DELETE pre BPS • x = vparent; • BALANCE (x, v); • delete v; • 2)v má dvoch synov: • nájdeme maximum v ľavom podstromevrchola v (MAX) • vymeníme v a MAX • postupujeme podľa 1)
Balance • BALANCE (x, v) • { • if (x != NULL) • { • if ((v->key < x->key) && (x->b 0)) • { • x->b = x->b – 1; • if (x->b == 0) BALANCE (x->parent, v); • } • if (v->key > x->key) and (x->b 0) • { • x->b = x->b + 1; • if (x->b == 0) BALANCE (x->parent, v); • } • if (v->key > x->key) and (x->b == 1) situácia A alebo B • if (v->key < x->key) and (x->b == -1) situácia symetr. ku A alebo B • if (vyvažovanie A resp. B &&x->parent nevyváženosť) BALANCE (xparent, v); • } • }
Zložitosť pre AVL stromy • Daná výškou stromu • Asymptotická zložitosť operácií: O(log n) • Maximálny počet prechodov: 1.44 * lg(n) • Insert: možná jedna-dve rotácie, priemerne 1 rotácia na 2 vloženia • Delete: možnosť veľa rotácií pre jedno vymazanie, priemerne 1 rotácia na 5 vymazaní
Červeno-čierne stromy • Iné pravidlá pre vyváženie: • Každý vrchol je čierny alebo červený • Koreň stromu je čierny • Všetky listy stromu sú čierne • Synovia červeného vrcholu sú čierny • Každá cesta z vrcholu v do nejakého listu má rovnaký počet čiernych vrcholov
Vlastnosti • bh(n) – počet čiernych vrcholov na ceste z vrchola n do listu • R-B strom má výšku maximálne 2.lg(n+1) • Dĺžka najdlhšej cesty z koreňa do listu nie je väčšia ako dvojnásobná dĺžka najkratšej cesty z koreňa do listu • Na jednej ceste z vrcholu do listu nemôže byť viac červených vrcholov ako čiernych
Vkladanie • BPS vkladanie • Vlož vrchol ako červený • Postupuj od vloženého vrcholu až do koreňa hľadaj porušenie pravidla 3, 4 a 5 • Ak je porušené pravidlo prefarbuj alebo rotuj v okolí daného vrchola
Vkladanie 2 • RB_INSERT (T, x) • { • BPS_INSERT(T, x); • x->color = RED; • while (x != T->root && x->parent->color == RED) • { • if (x->parent == x->parent->parent->left) • { • Y = x->parent->parent->right; • if (y->color == RED) • { • x->parent->color = BLACK; • y->color = BLACK; • x->parent->parent = RED; • x = x->parent->parent; • } • else • { • if (x == x->parent->right) • { • x = x->parent; • LeftRotate(x); • } • x->parent->color = BLACK; • x->parent->parent->color = RED; • RightRotate(x->parent->parent); • } • } • else • { • // symetricky, vymenit left a right • } • } • } Prípad 1 Prípad 2 Prípad 3
Vymazanie vrcholu z R-B • Mazanie ako pri BPS • Ak zmazaný vrchol bol červený, nič sa nedeje • Ak je vrchol čierny, treba urobiť spätný prechod od mazaného vrcholu do koreňa a opravovať podmienky • Použitie rotácií a prefarbovania
B-stromy • Každý vrchol obsahuje aspoň n synov • Každý vrchol obsahuje najviac 2n vrcholov • Všetky listy sú na jednej úrovni • V každom vrchole je počet kľúčov o 1 menší ako počet potomkov • n – rád stromu • Výška stromu – O(lognN) • Netreba tak často vyvažovať • Plytvá miestom – vhodné pre velké bloky dát
B-stromy 2 • Vkladanie – kontrola na prekročenie limitu 2.n • Mazanie – kontrola na počet potomkov aspoň n • Náprava – rozdeľovanie, spájanie kľúčov, rotácie
Halda • Stromová štruktúra • Ak vrchol B je potomok vrcholu A, tak key(A) key(B) (key(A) key(B)) • Vytvorenie v O(n) • Použitie: • Triedenie • Prehľadávanie utriedených množín • Grafové algoritmy • Prioritná fronta
Zásobník • Princíp Last In First Out (LIFO), posledný uložený prvok sa vyberie ako prvý • Operácie (push, pop, peek, size, …) • Použitie: • spracovanie výrazov • manažment pämate • skladanie transformácií
Fronta • Princíp FirstInFirstOut (FIFO) • Prvky sa vkladajú na jednom konci, vyberajú na druhom • Vyberá sa vždy „najstarší“ prvok vo fronte • Prioritná fronta: • Pridaj prvok s definovanou prioritou • Vyber prvok s najvyššou prioritou
Oknové a bodové požiadavky • Či sa bod nachádza v danej množine • Všetky body v danej množine • Všetky objekty v danom okne • Priesečníky okien • Požiadavka: • Rozmer priestoru • Prislušná dvojica objektov • Typ požiadavky
Intervalové stromy • Binárny strom pre požiadavku interval/bod • Vstup: množinaSuzavretých jednorozmerných intervalov • Požiadavka: reálna hodnota xq • Výstup: všetky intervalyIStaké, žexqI • S = {[li, ri] pre i = 1, …, n}
Vrcholy stromu • Xmed–medián, ktorým bola rozdelená množina intervalov v tomto vrchole • Lmed- usporiadaná množina ľavých koncových bodov intervalov, ktoré obsahovali Xmed • Rmed- usporiadaná množina pravých koncových bodov intervalov, ktoré obsahovali Xmed • Smerníky na dvoch synov • struct IntervalTreeNode • { • float xmed; • vector<float> Lmed; • vector<float> Rmed; • IntervalTreeNode * left; • IntervalTreeNode * right; • } struct IntervalTree { IntervalTreeNode* root; }
Vytvorenie stromu • Pre n intervalov, intervalový strom sa dá vytvoriť v čase O(n.log n) a zaberie O(n) pamäte • IntervalTreeNodeConstruct(S) • { • if (S == 0) return NULL; • v = new IntervalTreeNode; • v->xmed = Median(S); • Smed = HitSegments(v->xmed, S); • L = LeftSegments(v->xmed, S); • R = RightSegments(v->xmed, S); • v->Lmed = SortLeftEndPoints(Smed); • v->Rmed = SortRightEndPoints(Smed) • v->left = IntervalTreeNodeConstruct(L); • v->right = IntervalTreeNodeConstruct(R); • return v; • } • IntervalTreeConstruct(S) • { • T = new IntervalTree; • T = IntervalTreeNodeConstruct(S); • return T; • }
Vyhľadanie • IntervalStabbing(v, xq) • { • D = new List; • if (xq < v->xmed) • { • L = v->Lmed; • F = L.first; • while (F != NULL && F < xq) • { • D.insert(Seg(F)); • F = L.next; • } • D1 = IntervalStabbing(v->left, xq); • D.add(D1); • } • else • { • R = v->Rmed; • F = R.first; • while (F != NULL && F > xq) • { • D.insert(Seg(F)); • F = R.next; • } • D2 = IntervalStabbing(v->right, xq); • D.add(D2); • } • return D; • } • Pre množinun intervalov, požiadavka na vyhľadanie vráti k intervalov v čase O(k + log n)
Segmentové (úsečkové) stromy • Vyhľadanie všetkých úsečiek z danej množiny, ktoré sa pretínajú s danou zvislou priamkou • Vstup: množinaúsečiek Sv rovine • Požiadavka: zvislá priamka l • Výstup: všetky úsečkysS, ktoré pretínajú l • S = {s1, s2, …, sn}
Prehľadávací strom • Usporiadame x-ové súradnice koncových bodov úsečiek,E = {e1, e2, …, e2n} • Rozdeľ E na intervaly[-∞,e1], …, [e2n-1, e2n], [e2n, ∞] • Základné intervaly budú listy prehľadávacieho stromu • SearchTreeNodeConstruct (S) • { • if (|S|== 0) return NULL; • v = new SegmentTreeNode; • n = |S|;m = n / 2; • (L, R) = Split(S, m); • v->key = S.get(m); • v->istart = S.get(1); • v->iend = S.get(n); • v->left = SearchTreeNodeConstruct(L); • v->right = SearchTreeNodeConstruct(R); • return v; • } • struct SegmentTreeNode • { • float key; • float istart; • float iend; • List L; • IntervalTreeNode* left; • IntervalTreeNode* right; • }
Segmentový strom • Máme malé intervaly uložené v stromovej štruktúre • Naplníme tento strom príslušnosťou ku úsečkám tak, aby každá úsečka bola pokrytá čo najmenším počtom intervalov (vrcholov) prehľadávacieho stromu • Vytvorenie seg. stromu má časovú zložitosť O(n.logn), strom využije O(n.logn) pamäte
Vytvorenie segmentového stromu • InsertSegment(v, s) • { • if (v == NULL) return; • if ([v->istart, v->iend] s == 0) return; • if ([v->istart, v->iend] s) • { • v->l.add(s); • } • else • { • InsertSegment(v->left, s); • InsertSegment(v->right, s); • } • } struct SegmentTree { SegmentTreeNode* root; } • BuildSegmentTree(S) • { • Sx = S.SortX(); • Sx.ExtendInfinity(); • New T = new SegementTree; • T->root = SearchTreeNodeConstruct(Sx); • while (S.first != 0) • { • InsertSegment(T->root, S.first); • S.deleteFirst(); • } • }
Vyhľadanie • Pre n úsečiek v rovine, nájdenie všetkých úsečiek ktoré pretínajú danú zvislú priamku má časovú náročnosť O(k + logn), kdekje počet nájdených úsečiek • StabbingQuery(v, l) • { • List L; • If (v != NULL && l [v->istart, v->iend]) • { • L = v.L; • L1 = StabbingQuery(v->left, l); • L2 = StabbingQuery(v->right, l); • L.add(L1); • L.add(L2); • return L; • } • }
Viacrozmerné segmentové stromy • MLSegmentTree(B, d) • { • S = B.FirstSegmentsExtract; • T = SegmentTreeConstruct(S); • T.dim = d; • if (d > 1) • { • N = T.GetAllNodes; • while (|N|!=0) • { • u = N.First; • N.DeleteFirst; • L = u->L; • List B; • while (|L| != 0) • { • s = L.First; • L.DeleteFirst; • B.add(s.Box(d − 1)); • } • u->tree = MLSegmentTree(B, d − 1); • } • } • return T; • } • Vstup: množina d-rozmerných obdĺžnikov S rovnobežných so súradnicovými osami • Požiadavka: bod q z Rd • Výstup: množina boxov z S ktoré obsahujú bod q • V každom vrchole d-rozmerného stromu sa môže nachádzať d-1 rozmerný strom
Viacrozmerné segmentové stromy 2 Čas zostrojenia: O(n.logdn) Pamäť: O(n.logdn) Požiadavka: O(k + logdn) • MLSegmentTreeQuery(T, q, d) • { • if (d == 1) • { • L = StabbingQuery(T, q); • return L; • } • else • { • List A; • L = SearchTreeQuery(T, q.First); • while (|L|!= 0) • { • t = (L.First)->tree; • B = MLSegmentTreeQuery(t, q.Rest, d − 1); • A.ListAdd(B); • L.DeleteFirst; • } • return A; • } • }
Range stromy • Vstup: množina bodov S v Rd • Požiadavka: obdĺžnik B z Rd rovnobežný so súradnicovými osami • Výstup: Množina bodov z S, ktoré patria obdĺžniku B • Podobná metóda ako pri segmentových stromoch • Vytvorenie prehľadávacieho stromu na základe súradníc bodov • Väčšinou d ≥ 2
Range stromy 2 • Pre jednorozmerný prípad – hľadanie bodov vo vnútri intervalu • Vytvorenie prehľadávacieho stromu
Range stromy 3 • Vrchol d-rozmerného range stromu môže obsahovať d-1 rozmerný range strom • RangeTreeConstruct(S, d) • { • Sf= S.FirstCoordElements; • Sf.Sort; • T = SearchTree(Sf); • T->dim = d; • if (d > 1) • { • N = T->GetAllNodes; • while (|N| != 0) • { • u = N.First;N.DeleteFirst; • L = u->GetAllPoints; • List D; • while (|L|!= 0) • { • x = L.First;L.DeleteFirst; • D.add(x.Point(d − 1)); • } • u->tree = RangeTreeConstruct(D, d − 1); • } • } • return T; • }
Zložitosť • d-rozmerný range strompre množinunbodov v Rdmôže byť vytvorený v čase O(n.log(d−1)n) a spotrebuje O(n.log(d−1)n) pamäte • Požiadavka na zistenie bodov vnútri d-rozmerného boxu má časovú náročnosť O(k + logdn)
AABB/AABB • Vstup: množina S 2D intervalov (obdĺžnikov) • Požiadavka: 2D interval B • Výstup: všetky AABB z S ktoré majú prienik s B • Tri možnosti prieniku: • Bje vnútriBi • RohBileží vB • StranaBipretínaB a žiadny koncový bod Binie je vB
AABB/AABB 2 • Prípad 1. – 2D segmentovýstrom. Hľadáme do ktorých boxov Biz S padnú štyri rohy B • čas O(k1 + log2n) a pamäť O(n + log2n) • Prípad 2. – Range stromv 2D. Hľadáme rohy boxov Biktoré padnú do obdĺžnika B • čas O(k2 + log2n) a pamäť O(n.logn). • Prípad 3. – Nová požiadavka: • Vstup: Množina horizontálnych čiar v 2D • Požiadavka: Vertikálna čiara s v 2D • Output: Všetky úsečky pretínajúce s
AABB/AABB – Prípad 3 • V smere x hľadanie častí s priesečníkom, v smere y časti patriace do intervalu • Kombinácia intervalového a range stromu • Najprv vytvoríme intervalový strom • Lmed a Rmed nahradíme prislušnými 2D range stromami • Čas O(k3 + log2n), pamäť O(n.logn)
Rozšírenie • Nielen pre geometrické dáta • Vyhľadávanie v d-rozmerných dátach • Podľa požiadaviek, hľadanie dát v danom intervale, hľadanie približných dát • Porovnávanie objektov
koniec (-: florek@sccg.sk